Skip to content

.

91a3042
Select commit
Loading
Failed to load commit list.
Draft

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

.
91a3042
Select commit
Loading
Failed to load commit list.
@sentry/warden / warden: find-bugs completed Mar 13, 2026 in 27m 34s

5 issues

find-bugs: Found 5 issues (3 high, 1 medium, 1 low)

High

get_start_span_function returns incompatible function signature when streaming is enabled - `sentry_sdk/ai/utils.py:537-540`

When span streaming is enabled or the current span is a StreamedSpan, get_start_span_function() returns sentry_sdk.traces.start_span which only accepts (name, attributes, parent_span, active). However, all callers (anthropic, google_genai, litellm, mcp, openai_agents, pydantic_ai integrations) pass legacy parameters like op=... and origin=... which are not accepted by the streaming API. This will cause a TypeError at runtime when span streaming is enabled.

Also found at:

  • sentry_sdk/integrations/asgi.py:238-241
  • sentry_sdk/integrations/httpx.py:116-118
  • sentry_sdk/integrations/stdlib.py:175-177
StreamedSpan.set_status() method does not exist, causing AttributeError on SQL errors - `sentry_sdk/integrations/sqlalchemy.py:102`

In the _handle_error function, when a StreamedSpan is detected, the code calls span.set_status(SpanStatus.ERROR). However, the StreamedSpan class does not have a set_status() method - it only has a status property with a setter. This will raise an AttributeError when a SQLAlchemy error occurs in streaming mode, preventing proper error status tracking and potentially breaking error handling.

Also found at:

  • sentry_sdk/integrations/celery/__init__.py:104-105
NoOpStreamedSpan._get_trace_context() will crash due to missing instance attributes - `sentry_sdk/scope.py:609`

When a NoOpStreamedSpan is set as the scope's active span and scope.get_trace_context() is called, line 609 will invoke NoOpStreamedSpan._get_trace_context(). However, NoOpStreamedSpan does not override this method and inherits it from StreamedSpan. The inherited method accesses self._parent_span_id and self._attributes, which are never initialized in NoOpStreamedSpan.init (it doesn't call super().init()). It also calls self._dynamic_sampling_context() which tries to access self._segment._get_baggage() but _segment is None. This will result in AttributeError crashes when events are captured while a NoOpStreamedSpan is active.

Also found at:

  • sentry_sdk/traces.py:583

Medium

Spans not closed if Redis command raises exception in async client - `sentry_sdk/integrations/redis/_async_common.py:145-147`

In _sentry_execute_command, if old_execute_command raises an exception, db_span.__exit__() and cache_span.__exit__() are never called because there's no try/finally block. This causes span resources to leak and spans to never be properly closed/sent. The sync version in _sync_common.py correctly uses try/finally (lines 151-160) to ensure spans are always closed.

Also found at:

  • sentry_sdk/integrations/redis/_async_common.py:147

Low

UnboundLocalError when Redis command fails with cache span active - `sentry_sdk/integrations/redis/_sync_common.py:158`

In sentry_patched_execute_command, if old_execute_command raises an exception and cache_span is set, the finally block will attempt to access value in _set_cache_data, which will raise UnboundLocalError. While capture_internal_exceptions() catches this, it causes spurious internal errors to be logged and masks the intent of the error handling. The async version in _async_common.py has the opposite problem - it doesn't wrap in try/finally, so spans leak on exception.


Duration: 27m 24s · Tokens: 20.7M in / 179.7k out · Cost: $29.65 (+extraction: $0.03, +merge: $0.01, +fix_gate: $0.01)

Annotations

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

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

get_start_span_function returns incompatible function signature when streaming is enabled

When span streaming is enabled or the current span is a StreamedSpan, `get_start_span_function()` returns `sentry_sdk.traces.start_span` which only accepts `(name, attributes, parent_span, active)`. However, all callers (anthropic, google_genai, litellm, mcp, openai_agents, pydantic_ai integrations) pass legacy parameters like `op=...` and `origin=...` which are not accepted by the streaming API. This will cause a TypeError at runtime when span streaming is enabled.

Check failure on line 241 in sentry_sdk/integrations/asgi.py

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

[5CY-WHD] get_start_span_function returns incompatible function signature when streaming is enabled (additional location)

When span streaming is enabled or the current span is a StreamedSpan, `get_start_span_function()` returns `sentry_sdk.traces.start_span` which only accepts `(name, attributes, parent_span, active)`. However, all callers (anthropic, google_genai, litellm, mcp, openai_agents, pydantic_ai integrations) pass legacy parameters like `op=...` and `origin=...` which are not accepted by the streaming API. This will cause a TypeError at runtime when span streaming is enabled.

Check failure on line 118 in sentry_sdk/integrations/httpx.py

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

[5CY-WHD] get_start_span_function returns incompatible function signature when streaming is enabled (additional location)

When span streaming is enabled or the current span is a StreamedSpan, `get_start_span_function()` returns `sentry_sdk.traces.start_span` which only accepts `(name, attributes, parent_span, active)`. However, all callers (anthropic, google_genai, litellm, mcp, openai_agents, pydantic_ai integrations) pass legacy parameters like `op=...` and `origin=...` which are not accepted by the streaming API. This will cause a TypeError at runtime when span streaming is enabled.

Check failure on line 177 in sentry_sdk/integrations/stdlib.py

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

[5CY-WHD] get_start_span_function returns incompatible function signature when streaming is enabled (additional location)

When span streaming is enabled or the current span is a StreamedSpan, `get_start_span_function()` returns `sentry_sdk.traces.start_span` which only accepts `(name, attributes, parent_span, active)`. However, all callers (anthropic, google_genai, litellm, mcp, openai_agents, pydantic_ai integrations) pass legacy parameters like `op=...` and `origin=...` which are not accepted by the streaming API. This will cause a TypeError at runtime when span streaming is enabled.

Check failure on line 102 in sentry_sdk/integrations/sqlalchemy.py

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

StreamedSpan.set_status() method does not exist, causing AttributeError on SQL errors

In the `_handle_error` function, when a StreamedSpan is detected, the code calls `span.set_status(SpanStatus.ERROR)`. However, the `StreamedSpan` class does not have a `set_status()` method - it only has a `status` property with a setter. This will raise an `AttributeError` when a SQLAlchemy error occurs in streaming mode, preventing proper error status tracking and potentially breaking error handling.

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

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

[6GR-KXG] StreamedSpan.set_status() method does not exist, causing AttributeError on SQL errors (additional location)

In the `_handle_error` function, when a StreamedSpan is detected, the code calls `span.set_status(SpanStatus.ERROR)`. However, the `StreamedSpan` class does not have a `set_status()` method - it only has a `status` property with a setter. This will raise an `AttributeError` when a SQLAlchemy error occurs in streaming mode, preventing proper error status tracking and potentially breaking error handling.

Check failure on line 609 in sentry_sdk/scope.py

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

NoOpStreamedSpan._get_trace_context() will crash due to missing instance attributes

When a NoOpStreamedSpan is set as the scope's active span and scope.get_trace_context() is called, line 609 will invoke NoOpStreamedSpan._get_trace_context(). However, NoOpStreamedSpan does not override this method and inherits it from StreamedSpan. The inherited method accesses self._parent_span_id and self._attributes, which are never initialized in NoOpStreamedSpan.__init__ (it doesn't call super().__init__()). It also calls self._dynamic_sampling_context() which tries to access self._segment._get_baggage() but _segment is None. This will result in AttributeError crashes when events are captured while a NoOpStreamedSpan is active.

Check failure on line 583 in sentry_sdk/traces.py

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

[WF5-QNQ] NoOpStreamedSpan._get_trace_context() will crash due to missing instance attributes (additional location)

When a NoOpStreamedSpan is set as the scope's active span and scope.get_trace_context() is called, line 609 will invoke NoOpStreamedSpan._get_trace_context(). However, NoOpStreamedSpan does not override this method and inherits it from StreamedSpan. The inherited method accesses self._parent_span_id and self._attributes, which are never initialized in NoOpStreamedSpan.__init__ (it doesn't call super().__init__()). It also calls self._dynamic_sampling_context() which tries to access self._segment._get_baggage() but _segment is None. This will result in AttributeError crashes when events are captured while a NoOpStreamedSpan is active.

Check warning on line 147 in sentry_sdk/integrations/redis/_async_common.py

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

Spans not closed if Redis command raises exception in async client

In `_sentry_execute_command`, if `old_execute_command` raises an exception, `db_span.__exit__()` and `cache_span.__exit__()` are never called because there's no try/finally block. This causes span resources to leak and spans to never be properly closed/sent. The sync version in `_sync_common.py` correctly uses `try/finally` (lines 151-160) to ensure spans are always closed.

Check warning on line 147 in sentry_sdk/integrations/redis/_async_common.py

See this annotation in the file changed.

@sentry-warden sentry-warden / warden: find-bugs

[UEP-MCD] Spans not closed if Redis command raises exception in async client (additional location)

In `_sentry_execute_command`, if `old_execute_command` raises an exception, `db_span.__exit__()` and `cache_span.__exit__()` are never called because there's no try/finally block. This causes span resources to leak and spans to never be properly closed/sent. The sync version in `_sync_common.py` correctly uses `try/finally` (lines 151-160) to ensure spans are always closed.