Skip to content

Merge branch 'release/2.52.0a7' into feat/span-first-2

a3000cf
Select commit
Loading
Failed to load commit list.
Sign in for the full log view
Closed

[do not merge] feat: Span streaming & new span API #5317

Merge branch 'release/2.52.0a7' into feat/span-first-2
a3000cf
Select commit
Loading
Failed to load commit list.
GitHub Actions / warden: code-review completed Feb 25, 2026 in 34m 31s

8 issues

code-review: Found 8 issues (4 high, 2 medium, 2 low)

High

API signature mismatch causes runtime TypeError when streaming is enabled - `sentry_sdk/ai/utils.py:542`

The get_start_span_function() returns sentry_sdk.traces.start_span when streaming mode is enabled, but this function only accepts name, attributes, and parent_span parameters. All callers (in litellm, anthropic, google_genai, langchain, mcp, pydantic_ai, openai_agents integrations) pass additional kwargs like op=... and origin=... which will cause TypeError: start_span() got an unexpected keyword argument 'op'. This breaks all AI integrations when span streaming is enabled.

Also found at:

  • sentry_sdk/integrations/celery/__init__.py:330-337
UnboundLocalError when Redis command raises exception - `sentry_sdk/integrations/redis/_sync_common.py:148`

In the finally block (line 143-150), the code references value when calling _set_cache_data(cache_span, self, cache_properties, value) on line 148. However, if old_execute_command on line 142 raises an exception, value is never assigned, causing an UnboundLocalError. This will crash the Redis instrumentation when any Redis operation fails, breaking error handling.

Also found at:

  • sentry_sdk/integrations/redis/_async_common.py:135-146
StreamedSpan created but never started before finish() is called - `sentry_sdk/integrations/strawberry.py:192-208`

The graphql_span is created via sentry_sdk.traces.start_span() in span streaming mode (line 192) but is never started before yield. When self.graphql_span.finish() is called at line 234, it will trigger __exit__ which accesses self._context_manager_state - an attribute that is only set by __enter__/start(). This will cause an AttributeError: 'StreamedSpan' object has no attribute '_context_manager_state' at runtime when span streaming is enabled.

Also found at:

  • sentry_sdk/integrations/strawberry.py:239-246
  • sentry_sdk/integrations/strawberry.py:261-266
Type annotation `dict[str, Any]` incompatible with Python 3.6-3.8 - `sentry_sdk/tracing_utils.py:478`

The type annotation dict[str, Any] uses PEP 585 syntax which is only available in Python 3.9+. However, the SDK supports Python >= 3.6 (as declared in setup.py). On Python 3.6-3.8, this will cause a TypeError at runtime when the annotation is evaluated. The rest of the codebase consistently uses Dict[str, Any] from the typing module (e.g., lines 384, 453, 536 in this file).

Medium

Source code information lost for StreamedSpan due to modification after span.end() - `sentry_sdk/integrations/stdlib.py:183-189`

In streaming mode, span.end() captures and queues the span for sending via scope._capture_span(self) before returning. The add_http_request_source() call on line 189 happens after span.end() completes, so any attributes it sets (source code information like code.lineno, code.filepath, etc.) will not be included in the sent span data. This is different from the legacy path where spans are only sent when the transaction finishes.

NoOpStreamedSpan records lost event even when span was never active - `sentry_sdk/traces.py:714-718`

In NoOpStreamedSpan.__exit__, the lost event is recorded via transport.record_lost_event() (lines 714-718) before checking if self._scope is None (line 720). When a NoOpStreamedSpan is created with scope=None, the __enter__ method returns early without doing anything, but __exit__ will still record a lost event. This leads to incorrect metrics, as spans that were never 'started' (because they had no scope) will be counted as lost.

Low

Unused json import adds unnecessary dependency - `sentry_sdk/_span_batcher.py:1`

The json module is imported on line 1 but never used anywhere in the file. The json={...} on line 132 is a parameter name for PayloadRef, not a use of the json module. This is dead code that should be removed.

Span captured before _finished flag is set, allowing duplicate span capture - `sentry_sdk/traces.py:441-444`

In _end(), the span is captured via scope._capture_span(self) on line 442 before self._finished = True is set on line 444. If _capture_span triggers any code that could call _end() again (e.g., through callbacks or async operations), the span could be captured multiple times before the _finished flag prevents it. The _finished check should happen at the start of _end() and be set before capture to ensure proper idempotency.


Duration: 34m 26s · Tokens: 14.1M in / 161.1k out · Cost: $18.82 (+extraction: $0.02, +merge: $0.00)

Annotations

Check failure on line 542 in sentry_sdk/ai/utils.py

See this annotation in the file changed.

@github-actions github-actions / warden: code-review

API signature mismatch causes runtime TypeError when streaming is enabled

The `get_start_span_function()` returns `sentry_sdk.traces.start_span` when streaming mode is enabled, but this function only accepts `name`, `attributes`, and `parent_span` parameters. All callers (in litellm, anthropic, google_genai, langchain, mcp, pydantic_ai, openai_agents integrations) pass additional kwargs like `op=...` and `origin=...` which will cause `TypeError: start_span() got an unexpected keyword argument 'op'`. This breaks all AI integrations when span streaming is enabled.

Check failure on line 337 in sentry_sdk/integrations/celery/__init__.py

See this annotation in the file changed.

@github-actions github-actions / warden: code-review

[3PL-QKR] API signature mismatch causes runtime TypeError when streaming is enabled (additional location)

The `get_start_span_function()` returns `sentry_sdk.traces.start_span` when streaming mode is enabled, but this function only accepts `name`, `attributes`, and `parent_span` parameters. All callers (in litellm, anthropic, google_genai, langchain, mcp, pydantic_ai, openai_agents integrations) pass additional kwargs like `op=...` and `origin=...` which will cause `TypeError: start_span() got an unexpected keyword argument 'op'`. This breaks all AI integrations when span streaming is enabled.

Check failure on line 148 in sentry_sdk/integrations/redis/_sync_common.py

See this annotation in the file changed.

@github-actions github-actions / warden: code-review

UnboundLocalError when Redis command raises exception

In the `finally` block (line 143-150), the code references `value` when calling `_set_cache_data(cache_span, self, cache_properties, value)` on line 148. However, if `old_execute_command` on line 142 raises an exception, `value` is never assigned, causing an `UnboundLocalError`. This will crash the Redis instrumentation when any Redis operation fails, breaking error handling.

Check failure on line 146 in sentry_sdk/integrations/redis/_async_common.py

See this annotation in the file changed.

@github-actions github-actions / warden: code-review

[KYX-6X4] UnboundLocalError when Redis command raises exception (additional location)

In the `finally` block (line 143-150), the code references `value` when calling `_set_cache_data(cache_span, self, cache_properties, value)` on line 148. However, if `old_execute_command` on line 142 raises an exception, `value` is never assigned, causing an `UnboundLocalError`. This will crash the Redis instrumentation when any Redis operation fails, breaking error handling.

Check failure on line 208 in sentry_sdk/integrations/strawberry.py

See this annotation in the file changed.

@github-actions github-actions / warden: code-review

StreamedSpan created but never started before finish() is called

The `graphql_span` is created via `sentry_sdk.traces.start_span()` in span streaming mode (line 192) but is never started before `yield`. When `self.graphql_span.finish()` is called at line 234, it will trigger `__exit__` which accesses `self._context_manager_state` - an attribute that is only set by `__enter__`/`start()`. This will cause an `AttributeError: 'StreamedSpan' object has no attribute '_context_manager_state'` at runtime when span streaming is enabled.

Check failure on line 246 in sentry_sdk/integrations/strawberry.py

See this annotation in the file changed.

@github-actions github-actions / warden: code-review

[UUA-HKE] StreamedSpan created but never started before finish() is called (additional location)

The `graphql_span` is created via `sentry_sdk.traces.start_span()` in span streaming mode (line 192) but is never started before `yield`. When `self.graphql_span.finish()` is called at line 234, it will trigger `__exit__` which accesses `self._context_manager_state` - an attribute that is only set by `__enter__`/`start()`. This will cause an `AttributeError: 'StreamedSpan' object has no attribute '_context_manager_state'` at runtime when span streaming is enabled.

Check failure on line 266 in sentry_sdk/integrations/strawberry.py

See this annotation in the file changed.

@github-actions github-actions / warden: code-review

[UUA-HKE] StreamedSpan created but never started before finish() is called (additional location)

The `graphql_span` is created via `sentry_sdk.traces.start_span()` in span streaming mode (line 192) but is never started before `yield`. When `self.graphql_span.finish()` is called at line 234, it will trigger `__exit__` which accesses `self._context_manager_state` - an attribute that is only set by `__enter__`/`start()`. This will cause an `AttributeError: 'StreamedSpan' object has no attribute '_context_manager_state'` at runtime when span streaming is enabled.

Check failure on line 478 in sentry_sdk/tracing_utils.py

See this annotation in the file changed.

@github-actions github-actions / warden: code-review

Type annotation `dict[str, Any]` incompatible with Python 3.6-3.8

The type annotation `dict[str, Any]` uses PEP 585 syntax which is only available in Python 3.9+. However, the SDK supports Python >= 3.6 (as declared in setup.py). On Python 3.6-3.8, this will cause a `TypeError` at runtime when the annotation is evaluated. The rest of the codebase consistently uses `Dict[str, Any]` from the `typing` module (e.g., lines 384, 453, 536 in this file).

Check warning on line 189 in sentry_sdk/integrations/stdlib.py

See this annotation in the file changed.

@github-actions github-actions / warden: code-review

Source code information lost for StreamedSpan due to modification after span.end()

In streaming mode, `span.end()` captures and queues the span for sending via `scope._capture_span(self)` before returning. The `add_http_request_source()` call on line 189 happens after `span.end()` completes, so any attributes it sets (source code information like `code.lineno`, `code.filepath`, etc.) will not be included in the sent span data. This is different from the legacy path where spans are only sent when the transaction finishes.

Check warning on line 718 in sentry_sdk/traces.py

See this annotation in the file changed.

@github-actions github-actions / warden: code-review

NoOpStreamedSpan records lost event even when span was never active

In `NoOpStreamedSpan.__exit__`, the lost event is recorded via `transport.record_lost_event()` (lines 714-718) before checking if `self._scope is None` (line 720). When a `NoOpStreamedSpan` is created with `scope=None`, the `__enter__` method returns early without doing anything, but `__exit__` will still record a lost event. This leads to incorrect metrics, as spans that were never 'started' (because they had no scope) will be counted as lost.