Skip to content

another batch of fixes

8d0b013
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

another batch of fixes
8d0b013
Select commit
Loading
Failed to load commit list.
GitHub Actions / warden completed Feb 25, 2026 in 42m 5s

26 issues

High

AttributeError when calling set_transaction_name with NoOpStreamedSpan on scope - `sentry_sdk/scope.py:829-831`

The code calls self._span.segment.set_name(name) when self._span is an instance of StreamedSpan. However, NoOpStreamedSpan is a subclass of StreamedSpan that sets self.segment = None in its constructor. When self._span is a NoOpStreamedSpan, accessing self._span.segment.set_name(name) will raise AttributeError: 'NoneType' object has no attribute 'set_name'. The code should either check isinstance(self._span, NoOpStreamedSpan) first or use a null check on segment before calling methods on it.

Also found at:

  • sentry_sdk/traces.py:527-528
UnboundLocalError crash when exception occurs inside capture_internal_exceptions block - `sentry_sdk/integrations/celery/__init__.py:324-365`

The variable span_ctx is declared at line 324 but only assigned inside the capture_internal_exceptions() block (lines 337/360). If an exception occurs after transaction is assigned (e.g., at line 333-335 or 349-359) but before span_ctx is assigned, the exception is suppressed but span_ctx remains unbound. The check at line 362 only verifies transaction is not None, then line 365 with span_ctx: will raise UnboundLocalError, crashing the Celery task execution.

StreamedSpan imported under TYPE_CHECKING but used at runtime with isinstance() - `sentry_sdk/integrations/graphene.py:30`

StreamedSpan is imported inside the if TYPE_CHECKING: block (line 30), but it's used in an isinstance() check at runtime (line 167 in graphql_span). When span streaming is enabled and the finally block executes, a NameError: name 'StreamedSpan' is not defined will be raised because the import is not available at runtime.

Also found at:

  • sentry_sdk/integrations/stdlib.py:127
AttributeError when calling set_transaction_name with NoOpStreamedSpan as current span - `sentry_sdk/scope.py:828-831`

In set_transaction_name, the code checks isinstance(self._span, StreamedSpan) and then accesses self._span.segment.set_name(name). However, NoOpStreamedSpan (a subclass of StreamedSpan) sets segment = None in its constructor. Since isinstance() returns True for subclasses, when the current span is a NoOpStreamedSpan, accessing .segment.set_name() will raise AttributeError: 'NoneType' object has no attribute 'set_name'. This can occur when spans are filtered out via is_ignored_span() or when a span has no name.

Medium

_running_size not updated when MAX_BEFORE_FLUSH triggers flush - `sentry_sdk/_span_batcher.py:68-70`

When size + 1 >= MAX_BEFORE_FLUSH on line 68, the code returns early without updating _running_size[span.trace_id]. The span has already been added to _span_buffer (line 66), but its size is never tracked. If the flush event doesn't clear all buffers immediately (e.g., due to threading), subsequent byte-based flush decisions will underestimate the actual buffer size, potentially causing memory growth beyond the intended limit.

Also found at:

  • sentry_sdk/integrations/redis/utils.py:152-160
StreamedSpan status hardcoded to ERROR, ignoring actual status parameter - `sentry_sdk/integrations/celery/__init__.py:104-107`

When scope.span is a StreamedSpan, the code always sets SpanStatus.ERROR regardless of the actual status parameter passed to _set_status. This loses the distinction between "aborted" (for control flow exceptions like Retry, Ignore, Reject) and "internal_error" (for actual failures). Since StreamedSpan.set_status() accepts Union[SpanStatus, str] and handles both types correctly, the status parameter should be passed through.

Also found at:

  • sentry_sdk/integrations/strawberry.py:314-320
UnboundLocalError possible when span_streaming is enabled and span setup fails - `sentry_sdk/integrations/celery/__init__.py:324-337`

In _wrap_tracer, the span_ctx variable is declared with a type annotation but not initialized. In the span_streaming branch, if start_span() succeeds but any subsequent setter method (set_origin, set_source, set_op) throws an exception, the exception is suppressed by capture_internal_exceptions() but span_ctx is never assigned. Since transaction is not None, the code proceeds to with span_ctx: (line 365), causing an UnboundLocalError that crashes the Celery task.

Missing test coverage for httpx integration with span streaming mode - `sentry_sdk/integrations/httpx.py:64-67`

The httpx integration now supports both legacy tracing and the new span streaming mode (trace_lifecycle: stream), but the existing tests in tests/integrations/httpx/test_httpx.py only test the legacy mode using start_transaction(). The new StreamedSpan code paths (lines 64-67, 80-88, 145-148, 161-168) are not covered by any integration tests, meaning bugs in span attribute setting or the streaming workflow won't be caught.

Spans leak when Redis command raises exception - `sentry_sdk/integrations/redis/_async_common.py:135`

In _sentry_execute_command, spans are opened via __enter__() but closed via __exit__(None, None, None) without a try/finally block. If await old_execute_command() raises an exception, both db_span and cache_span will never have __exit__ called, leaving them unclosed. This can cause memory leaks and incorrect tracing data (spans that are never finished).

Also found at:

  • sentry_sdk/integrations/redis/_sync_common.py:135-146
Missing parent_span in on_parse causes orphaned span in streaming mode - `sentry_sdk/integrations/strawberry.py:261-262`

In on_parse() for streaming mode (line 261-262), sentry_sdk.traces.start_span() is called without the parent_span=self.graphql_span argument, unlike on_validate() (line 239-240) which correctly passes it. This inconsistency means the parsing span will be parented to whatever span is currently active on the scope rather than explicitly to self.graphql_span, potentially causing incorrect span hierarchy in the trace.

...and 16 more

4 skills analyzed
Skill Findings Duration Cost
code-review 11 32m 58s $16.33
find-bugs 15 17m 57s $28.60
skill-scanner 0 41m 48s $4.12
security-review 0 37m $9.25

Duration: 129m 42s · Tokens: 45.4M in / 453.2k out · Cost: $58.39 (+extraction: $0.06, +merge: $0.01, +dedup: $0.03)