[do not merge] feat: Span streaming & new span API #5551
4 issues
find-bugs: Found 4 issues (1 high, 2 medium, 1 low)
High
StreamedSpan.set_status() method doesn't exist, causing AttributeError - `sentry_sdk/integrations/sqlalchemy.py:102`
The code calls span.set_status(SpanStatus.ERROR) on a StreamedSpan instance at line 102, but StreamedSpan (defined in sentry_sdk/traces.py) does not have a set_status() method. It only has a status property setter. This will raise an AttributeError: 'StreamedSpan' object has no attribute 'set_status' when SQLAlchemy encounters an error and the span is a StreamedSpan instance. The legacy Span class (from tracing.py) does have set_status(), explaining why the else branch works.
Also found at:
sentry_sdk/integrations/httpx.py:117-119
Medium
Spans are leaked on exception in async Redis client execute_command - `sentry_sdk/integrations/redis/_async_common.py:145`
In _sentry_execute_command, spans are manually entered via __enter__() on lines 126 and 145, but the subsequent __exit__() calls (lines 152 and 156) are not wrapped in a try/finally block. If await old_execute_command(self, name, *args, **kwargs) raises an exception, both db_span and cache_span will never be properly closed. This differs from the synchronous version in _sync_common.py which correctly uses try/finally. Unclosed spans can cause memory leaks and incorrect tracing data.
Also found at:
sentry_sdk/integrations/redis/_sync_common.py:158
getresponse exception handling doesn't set span error status for StreamedSpan - `sentry_sdk/integrations/stdlib.py:181-185`
When real_getresponse() raises an exception (e.g., network timeout, connection refused), the finally block calls span.end() directly without setting the span status to 'error'. For StreamedSpan, the status defaults to 'ok' (set in traces.py:289), so exceptions will be incorrectly recorded as successful spans. The httpx integration avoids this by using with span_ctx as span: which triggers __exit__ and properly sets error status on exceptions.
Also found at:
sentry_sdk/integrations/strawberry.py:226
Low
NoOpStreamedSpan._to_traceparent() appears unreachable - `sentry_sdk/traces.py:692-697`
The new _to_traceparent() method on NoOpStreamedSpan (lines 692-697) is never called. Both call sites that invoke _to_traceparent() have guards: scope.get_traceparent() explicitly skips NoOpStreamedSpan instances via isinstance check, and StreamedSpan._iter_headers() returns early when _segment is None (always true for NoOpStreamedSpan). This appears to be defensive programming for future-proofing, but without test coverage, the intended behavior cannot be verified.
Duration: 23m 53s · Tokens: 14.2M in / 134.5k out · Cost: $20.44 (+extraction: $0.02, +merge: $0.00, +fix_gate: $0.01)
Annotations
Check failure on line 102 in sentry_sdk/integrations/sqlalchemy.py
sentry-warden / warden: find-bugs
StreamedSpan.set_status() method doesn't exist, causing AttributeError
The code calls `span.set_status(SpanStatus.ERROR)` on a `StreamedSpan` instance at line 102, but `StreamedSpan` (defined in `sentry_sdk/traces.py`) does not have a `set_status()` method. It only has a `status` property setter. This will raise an `AttributeError: 'StreamedSpan' object has no attribute 'set_status'` when SQLAlchemy encounters an error and the span is a `StreamedSpan` instance. The legacy `Span` class (from `tracing.py`) does have `set_status()`, explaining why the `else` branch works.
Check failure on line 119 in sentry_sdk/integrations/httpx.py
sentry-warden / warden: find-bugs
[G2Z-HE9] StreamedSpan.set_status() method doesn't exist, causing AttributeError (additional location)
The code calls `span.set_status(SpanStatus.ERROR)` on a `StreamedSpan` instance at line 102, but `StreamedSpan` (defined in `sentry_sdk/traces.py`) does not have a `set_status()` method. It only has a `status` property setter. This will raise an `AttributeError: 'StreamedSpan' object has no attribute 'set_status'` when SQLAlchemy encounters an error and the span is a `StreamedSpan` instance. The legacy `Span` class (from `tracing.py`) does have `set_status()`, explaining why the `else` branch works.
Check warning on line 145 in sentry_sdk/integrations/redis/_async_common.py
sentry-warden / warden: find-bugs
Spans are leaked on exception in async Redis client execute_command
In `_sentry_execute_command`, spans are manually entered via `__enter__()` on lines 126 and 145, but the subsequent `__exit__()` calls (lines 152 and 156) are not wrapped in a `try/finally` block. If `await old_execute_command(self, name, *args, **kwargs)` raises an exception, both `db_span` and `cache_span` will never be properly closed. This differs from the synchronous version in `_sync_common.py` which correctly uses `try/finally`. Unclosed spans can cause memory leaks and incorrect tracing data.
Check warning on line 158 in sentry_sdk/integrations/redis/_sync_common.py
sentry-warden / warden: find-bugs
[GZX-CKT] Spans are leaked on exception in async Redis client execute_command (additional location)
In `_sentry_execute_command`, spans are manually entered via `__enter__()` on lines 126 and 145, but the subsequent `__exit__()` calls (lines 152 and 156) are not wrapped in a `try/finally` block. If `await old_execute_command(self, name, *args, **kwargs)` raises an exception, both `db_span` and `cache_span` will never be properly closed. This differs from the synchronous version in `_sync_common.py` which correctly uses `try/finally`. Unclosed spans can cause memory leaks and incorrect tracing data.
Check warning on line 185 in sentry_sdk/integrations/stdlib.py
sentry-warden / warden: find-bugs
getresponse exception handling doesn't set span error status for StreamedSpan
When `real_getresponse()` raises an exception (e.g., network timeout, connection refused), the `finally` block calls `span.end()` directly without setting the span status to 'error'. For `StreamedSpan`, the status defaults to 'ok' (set in `traces.py:289`), so exceptions will be incorrectly recorded as successful spans. The httpx integration avoids this by using `with span_ctx as span:` which triggers `__exit__` and properly sets error status on exceptions.
Check warning on line 226 in sentry_sdk/integrations/strawberry.py
sentry-warden / warden: find-bugs
[EKT-XYG] getresponse exception handling doesn't set span error status for StreamedSpan (additional location)
When `real_getresponse()` raises an exception (e.g., network timeout, connection refused), the `finally` block calls `span.end()` directly without setting the span status to 'error'. For `StreamedSpan`, the status defaults to 'ok' (set in `traces.py:289`), so exceptions will be incorrectly recorded as successful spans. The httpx integration avoids this by using `with span_ctx as span:` which triggers `__exit__` and properly sets error status on exceptions.