Skip to content

more fixes

bf93c59
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

more fixes
bf93c59
Select commit
Loading
Failed to load commit list.
GitHub Actions / warden completed Feb 25, 2026 in 41m 58s

17 issues

High

StreamedSpan not imported at runtime causing NameError - `sentry_sdk/integrations/graphene.py:167`

The StreamedSpan class is only imported within the TYPE_CHECKING block (line 30), which means it's not available at runtime. When span streaming is enabled and the code reaches line 167 (if isinstance(_graphql_span, StreamedSpan):), it will raise NameError: name 'StreamedSpan' is not defined. Other integrations like celery/__init__.py and strawberry.py correctly import StreamedSpan at the top level, not just for type checking.

Also found at:

  • sentry_sdk/integrations/redis/utils.py:152-160
StreamedSpan created but not started - spans will silently fail to send - `sentry_sdk/integrations/rust_tracing.py:216-220`

When sentry_sdk.traces.start_span() is called (lines 216, 231), the returned StreamedSpan is assigned to scope.span but never started via start() or context manager entry. When on_close() later calls finish() (which calls end() -> __exit__()), the span will fail to end properly because _context_manager_state was never set. The error is silently caught by capture_internal_exceptions(), so spans created in streaming mode will never be sent to Sentry.

StreamedSpan is only imported for type checking but used at runtime - `sentry_sdk/integrations/graphene.py:167`

StreamedSpan is imported under TYPE_CHECKING (line 30) but is used in an isinstance() check at runtime (line 167). When span streaming mode is enabled, executing a GraphQL query will raise NameError: name 'StreamedSpan' is not defined when the code reaches the finally block.

Also found at:

  • sentry_sdk/integrations/anthropic.py:572-574

Medium

Redundant span serialization causes O(2n) serialization overhead - `sentry_sdk/_span_batcher.py:78-81`

Each span is serialized via _to_transport_format() twice: once in _estimate_size() during add() for size tracking, and again in _flush() when building the envelope. For high-throughput scenarios with many spans, this doubles the serialization cost. Consider caching the serialized form or computing size from pre-cached data.

StreamedSpan status always set to ERROR regardless of status parameter - `sentry_sdk/integrations/celery/__init__.py:104-105`

The _set_status function receives a status parameter (e.g., "aborted" or "internal_error"), but when the span is a StreamedSpan, it always sets SpanStatus.ERROR instead of using the passed status value. Since StreamedSpan.set_status() accepts Union[SpanStatus, str], it can handle string values directly. This causes control flow exceptions (which should have "aborted" status) to be incorrectly marked as "error", losing the semantic distinction between expected control flow aborts and actual errors.

Spans not closed on exception in async Redis execute_command - `sentry_sdk/integrations/redis/_async_common.py:135-146`

In _sentry_execute_command, both db_span and cache_span are entered manually via __enter__() but if await old_execute_command(self, name, *args, **kwargs) raises an exception, the corresponding __exit__() calls are never executed. This causes the spans to remain open indefinitely, leading to missing telemetry data and potential memory leaks. The exception info is also not passed to __exit__, so the span status won't reflect the error condition.

Also found at:

  • sentry_sdk/integrations/redis/_sync_common.py:135
Async resolve() missing set_op() and set_origin() for StreamedSpan - `sentry_sdk/integrations/strawberry.py:314-321`

In the async resolve() method, when self.graphql_span is a StreamedSpan, the code does not call span.set_op(OP.GRAPHQL_RESOLVE) or span.set_origin(StrawberryIntegration.origin). However, the sync resolve() method (lines 357-358) correctly sets both. This inconsistency means async GraphQL resolvers will have incomplete span metadata in streaming mode, potentially affecting trace visualization and filtering in Sentry.

Converting None sample_rate to string produces "None" literal in baggage - `sentry_sdk/scope.py:1306`

_update_sample_rate_from_segment converts span.sample_rate to string without checking for None. When _set_sampling_decision returns early (e.g., tracing disabled, invalid sample rate, or rate is 0), sample_rate remains None. This causes str(None) = "None" to be stored in baggage's sentry_items["sample_rate"], which propagates an invalid value through trace context that downstream services may fail to parse correctly.

Byte size tracking skipped when count-based flush threshold is reached - `sentry_sdk/_span_batcher.py:68-70`

When size + 1 >= self.MAX_BEFORE_FLUSH (line 68), the span is added to _span_buffer at line 66 but the function returns at line 70 without updating _running_size. This means the byte size of that span is never tracked. If the flush thread is delayed or another span arrives before the flush completes, the byte-size tracking will undercount the actual buffer size, potentially allowing more data to accumulate than intended before a byte-based flush.

Also found at:

  • sentry_sdk/integrations/celery/__init__.py:330-337
Async httpx client has inconsistent baggage handling that can cause duplicate sentry items - `sentry_sdk/integrations/httpx.py:186-192`

The async httpx client uses inline baggage handling (lines 186-192) that differs from the sync client's add_sentry_baggage_to_headers() helper. The async implementation appends baggage with += without first stripping existing sentry items, while the sync client properly strips them. This can cause duplicate sentry baggage items when outgoing requests already have sentry-prefixed baggage, potentially causing parsing issues or exceeding header size limits on downstream services.

...and 7 more

4 skills analyzed
Skill Findings Duration Cost
code-review 9 17m 54s $18.88
find-bugs 8 35m 50s $28.69
skill-scanner 0 19m 24s $7.89
security-review 0 41m 37s $9.41

Duration: 114m 44s · Tokens: 45.0M in / 450.7k out · Cost: $64.96 (+extraction: $0.05, +merge: $0.01, +dedup: $0.03)