Skip to content

.

4b2147d
Select commit
Loading
Failed to load commit list.
Draft

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

.
4b2147d
Select commit
Loading
Failed to load commit list.
@sentry/warden / warden completed Mar 16, 2026 in 33m 13s

10 issues

High

Swapped method assignments cause AttributeError at runtime - `sentry_sdk/integrations/openai_agents/utils.py:55-58`

The code assigns span.set_attribute for Span instances and span.set_data for StreamedSpan instances, but these methods don't exist on those respective classes. Span (from tracing.py) has set_data() but not set_attribute(), while StreamedSpan (from traces.py) has set_attribute() but not set_data(). This will cause an AttributeError at runtime when _record_exception_on_span is called.

Also found at:

  • sentry_sdk/integrations/sqlalchemy.py:102
UnboundLocalError when Redis command raises exception - `sentry_sdk/integrations/redis/_sync_common.py:158`

In sentry_patched_execute_command, the value variable is assigned inside the try block (line 152), but it's accessed in the finally block (line 158). If old_execute_command raises an exception, value is never assigned, causing an UnboundLocalError when _set_cache_data(cache_span, self, cache_properties, value) is called. This will mask the original Redis exception with an internal error.

Method assignment is inverted: Span doesn't have set_attribute, StreamedSpan doesn't have set_data - `sentry_sdk/integrations/openai_agents/utils.py:55-58`

The code incorrectly assigns span.set_attribute for Span instances and span.set_data for StreamedSpan instances. However, Span (in tracing.py) only has set_data() method (line 590), while StreamedSpan (in traces.py) only has set_attribute() method (line 406). This will cause an AttributeError at runtime when _record_exception_on_span is called, failing to record exception data on spans.

Also found at:

  • sentry_sdk/integrations/sqlalchemy.py:3
  • sentry_sdk/integrations/sqlalchemy.py:102

Medium

Span streaming creates spans for HTTP methods that should be ignored - `sentry_sdk/integrations/asgi.py:238-241`

In the span streaming branch (lines 218-241), when ty is 'http' but the method is NOT in http_methods_to_capture, the code still creates a span via sentry_sdk.traces.start_span() (line 238). In the legacy branch, transaction would remain None and nullcontext() would be used instead. This behavioral difference causes spans to be created and sent for HTTP methods that users have explicitly configured to ignore, potentially leading to excessive span volume and unexpected costs.

HTTP status code attribute missing in StreamedSpan path - `sentry_sdk/integrations/httpx.py:116-118`

In the synchronous send() function, when span_streaming is enabled, the HTTP response status code is not being set as an attribute on the span. The legacy Span.set_http_status() method sets SPANDATA.HTTP_STATUS_CODE (i.e., http.response.status_code), but the StreamedSpan path only sets the status string ("ok"/"error") and the "reason" attribute. This causes a data inconsistency where spans sent in streaming mode will be missing the HTTP status code, which is important telemetry data for HTTP client spans.

Also found at:

  • sentry_sdk/integrations/httpx.py:199-201
Missing exception handling causes span resource leaks in async Redis client - `sentry_sdk/integrations/redis/_async_common.py:145-147`

The async _sentry_execute_command function manually enters db_span and cache_span using __enter__() but lacks a try...finally block to ensure __exit__() is called when old_execute_command raises an exception. In contrast, the sync version in _sync_common.py wraps the call in try...finally. If the Redis command throws, spans won't be closed, the scope's active span won't be restored, and the error status won't be set on the spans.

StreamedSpan instances not closed when Anthropic API call fails - `sentry_sdk/integrations/anthropic.py:734-736`

The code change to isinstance(span, Span) excludes StreamedSpan instances from the error cleanup path. When span streaming is enabled and an Anthropic API call fails, set_span_errored() sets span.status = SpanStatus.ERROR ("error"), but the finally block only checks for SPANSTATUS.INTERNAL_ERROR ("internal_error") on legacy Span types. This causes StreamedSpan instances to remain unclosed when errors occur, potentially causing resource leaks and incorrect span timing in traces.

Also found at:

  • sentry_sdk/integrations/redis/_async_common.py:145-147
HTTP method filtering bypassed in span streaming mode - `sentry_sdk/integrations/asgi.py:238-241`

In the span streaming branch, spans are always created regardless of whether the HTTP method is in http_methods_to_capture. The legacy branch correctly skips transaction creation when ty == "http" and method not in self.http_methods_to_capture (resulting in transaction = None and nullcontext()). However, in span streaming mode, sentry_sdk.traces.start_span() is called unconditionally on line 238-240, creating spans for all HTTP requests including those that should be filtered out.

UnboundLocalError in finally block when Redis command raises exception - `sentry_sdk/integrations/redis/_sync_common.py:151-160`

When old_execute_command raises an exception, the finally block attempts to call _set_cache_data(cache_span, self, cache_properties, value) on line 158, but value is never assigned when an exception occurs. This results in an UnboundLocalError. While capture_internal_exceptions() suppresses this error, it masks the underlying bug and prevents proper cache span data from being recorded. The fix should initialize value = None before the try block.

Also found at:

  • sentry_sdk/integrations/redis/_async_common.py:147
HTTP status code attribute missing in streaming span mode - `sentry_sdk/integrations/stdlib.py:175-177`

When using span streaming mode, the getresponse handler sets the span status and reason but omits the HTTP status code attribute (SPANDATA.HTTP_STATUS_CODE). The legacy code path calls span.set_http_status(int(rv.status)) which sets both the span status and stores the actual status code value. In streaming mode, the status code value is lost, which degrades telemetry data quality for HTTP client spans.

Also found at:

  • sentry_sdk/integrations/httpx.py:116-118
  • sentry_sdk/integrations/httpx.py:199-201
4 skills analyzed
Skill Findings Duration Cost
code-review 5 23m 32s $13.01
find-bugs 5 15m 19s $20.91
skill-scanner 0 32m 56s $6.39
security-review 0 28m 6s $5.12

Duration: 99m 54s · Tokens: 30.5M in / 329.6k out · Cost: $45.53 (+extraction: $0.04, +merge: $0.01, +fix_gate: $0.02, +dedup: $0.03)