9 issues
find-bugs: Found 9 issues (2 high, 6 medium, 1 low)
High
StreamedSpan.set_status() method does not exist - will raise AttributeError - `sentry_sdk/integrations/sqlalchemy.py:102`
The code calls span.set_status(SpanStatus.ERROR) on a StreamedSpan instance at line 102, but StreamedSpan does not have a set_status() method. It only has a status property setter. This will cause an AttributeError at runtime when a SQL error occurs in streaming mode. Other integrations (e.g., tracing_utils.py:1140, celery/init.py:105) correctly use span.status = SpanStatus.ERROR for StreamedSpan.
Also found at:
sentry_sdk/integrations/celery/__init__.py:105
AttributeError when using legacy Span with new scope methods - `sentry_sdk/scope.py:586-610`
The code now calls _to_traceparent(), _to_baggage(), and _get_trace_context() on self.span, but self.span can be either a StreamedSpan (which has these private methods) or a legacy Span class (which only has to_traceparent(), to_baggage(), get_trace_context() without underscore prefix). When using the legacy non-streaming mode, calling these methods will raise AttributeError: 'Span' object has no attribute '_to_traceparent'.
Medium
StreamedSpan not closed on error in Anthropic integration - `sentry_sdk/integrations/anthropic.py:572-574`
When using span streaming mode, if an exception occurs during the Anthropic API call, the StreamedSpan is never closed. The isinstance(span, Span) check on line 572 only handles the legacy Span type. For StreamedSpan, _capture_exception() sets span.status = SpanStatus.ERROR, but since the type check fails, span.__exit__() is never called. This results in resource leaks and lost span data when errors occur in streaming mode.
Also found at:
sentry_sdk/integrations/anthropic.py:610-612
HTTP status code not recorded in span streaming mode - `sentry_sdk/integrations/asgi.py:289-294`
In the span streaming path, when a StreamedSpan receives an HTTP response, only a simplified 'error'/'ok' status is set (lines 290-294). The actual HTTP status code is never recorded as an attribute. In contrast, the legacy Span path calls set_http_status() which records the status code as http.response.status_code. This results in loss of HTTP status code telemetry data when using the new span streaming mode.
Also found at:
sentry_sdk/integrations/httpx.py:116-119sentry_sdk/integrations/stdlib.py:175-177
Missing exception handling causes spans to never close on Redis errors - `sentry_sdk/integrations/redis/_async_common.py:145-158`
In _sentry_execute_command, db_span.__enter__() and cache_span.__enter__() are called, but there's no try/finally block wrapping await old_execute_command(...). If the Redis command throws an exception, the spans' __exit__ methods will never be called. This causes spans to leak on the scope (corrupting parent-child relationships for subsequent spans) and prevents the spans from being sent to Sentry. The sync version correctly uses try/finally (see _sync_common.py lines 151-160).
Also found at:
sentry_sdk/integrations/redis/_sync_common.py:158
AttributeError when NoOpStreamedSpan._segment is accessed - `sentry_sdk/integrations/strawberry.py:225-226`
When span streaming is enabled but the span is either ignored or not sampled, sentry_sdk.traces.start_span() returns a NoOpStreamedSpan which sets _segment = None. The code at line 226 does isinstance(self.graphql_span, StreamedSpan) check which passes for NoOpStreamedSpan (it's a subclass), then accesses self.graphql_span._segment.set_attribute(...). Since _segment is None for NoOpStreamedSpan, this raises AttributeError: 'NoneType' object has no attribute 'set_attribute'. This will crash the GraphQL request handling when a span is unsampled or ignored.
Also found at:
sentry_sdk/scope.py:1286-1292
Duplicate timestamp initialization causes inconsistent timing - `sentry_sdk/traces.py:290-298`
The __init__ method initializes _start_timestamp, _timestamp, and _start_timestamp_monotonic_ns twice (lines 280-288 and 290-298 are identical). This is a copy-paste bug that causes _start_timestamp and _start_timestamp_monotonic_ns to be set twice with slightly different values, as time passes between the two calls. The second assignment overwrites the first, making span start times slightly later than they should be.
NoOpStreamedSpan records lost events multiple times when end() is called more than once - `sentry_sdk/traces.py:623-631`
The NoOpStreamedSpan._end() method calls record_lost_event() unconditionally without any guard to prevent duplicate execution. Unlike StreamedSpan._end() which checks if self._timestamp is not None: return, NoOpStreamedSpan has no such protection. This causes inaccurate lost event counting when: (1) end() is called explicitly and the span is also used as a context manager (both end() and __exit__ trigger _end()), or (2) end() is called multiple times.
Low
Slot `_sampled` declared but never initialized - `sentry_sdk/traces.py:233`
The _sampled slot is declared in __slots__ (line 233) but is never assigned a value in __init__. The sampled property always returns True without using this field. If any code attempts to access self._sampled directly, it will raise an AttributeError. This appears to be incomplete implementation of sampling support.
Duration: 29m 35s · Tokens: 24.5M in / 207.6k out · Cost: $33.33 (+extraction: $0.03, +merge: $0.01, +fix_gate: $0.01)
Annotations
Check failure on line 102 in sentry_sdk/integrations/sqlalchemy.py
github-actions / warden: find-bugs
StreamedSpan.set_status() method does not exist - will raise AttributeError
The code calls `span.set_status(SpanStatus.ERROR)` on a `StreamedSpan` instance at line 102, but `StreamedSpan` does not have a `set_status()` method. It only has a `status` property setter. This will cause an `AttributeError` at runtime when a SQL error occurs in streaming mode. Other integrations (e.g., tracing_utils.py:1140, celery/__init__.py:105) correctly use `span.status = SpanStatus.ERROR` for `StreamedSpan`.
Check failure on line 105 in sentry_sdk/integrations/celery/__init__.py
github-actions / warden: find-bugs
[3LQ-X5U] StreamedSpan.set_status() method does not exist - will raise AttributeError (additional location)
The code calls `span.set_status(SpanStatus.ERROR)` on a `StreamedSpan` instance at line 102, but `StreamedSpan` does not have a `set_status()` method. It only has a `status` property setter. This will cause an `AttributeError` at runtime when a SQL error occurs in streaming mode. Other integrations (e.g., tracing_utils.py:1140, celery/__init__.py:105) correctly use `span.status = SpanStatus.ERROR` for `StreamedSpan`.
Check failure on line 610 in sentry_sdk/scope.py
github-actions / warden: find-bugs
AttributeError when using legacy Span with new scope methods
The code now calls `_to_traceparent()`, `_to_baggage()`, and `_get_trace_context()` on `self.span`, but `self.span` can be either a `StreamedSpan` (which has these private methods) or a legacy `Span` class (which only has `to_traceparent()`, `to_baggage()`, `get_trace_context()` without underscore prefix). When using the legacy non-streaming mode, calling these methods will raise `AttributeError: 'Span' object has no attribute '_to_traceparent'`.
Check warning on line 574 in sentry_sdk/integrations/anthropic.py
github-actions / warden: find-bugs
StreamedSpan not closed on error in Anthropic integration
When using span streaming mode, if an exception occurs during the Anthropic API call, the StreamedSpan is never closed. The `isinstance(span, Span)` check on line 572 only handles the legacy `Span` type. For `StreamedSpan`, `_capture_exception()` sets `span.status = SpanStatus.ERROR`, but since the type check fails, `span.__exit__()` is never called. This results in resource leaks and lost span data when errors occur in streaming mode.
Check warning on line 612 in sentry_sdk/integrations/anthropic.py
github-actions / warden: find-bugs
[93W-HU4] StreamedSpan not closed on error in Anthropic integration (additional location)
When using span streaming mode, if an exception occurs during the Anthropic API call, the StreamedSpan is never closed. The `isinstance(span, Span)` check on line 572 only handles the legacy `Span` type. For `StreamedSpan`, `_capture_exception()` sets `span.status = SpanStatus.ERROR`, but since the type check fails, `span.__exit__()` is never called. This results in resource leaks and lost span data when errors occur in streaming mode.
Check warning on line 294 in sentry_sdk/integrations/asgi.py
github-actions / warden: find-bugs
HTTP status code not recorded in span streaming mode
In the span streaming path, when a StreamedSpan receives an HTTP response, only a simplified 'error'/'ok' status is set (lines 290-294). The actual HTTP status code is never recorded as an attribute. In contrast, the legacy Span path calls `set_http_status()` which records the status code as `http.response.status_code`. This results in loss of HTTP status code telemetry data when using the new span streaming mode.
Check warning on line 119 in sentry_sdk/integrations/httpx.py
github-actions / warden: find-bugs
[7W8-KHM] HTTP status code not recorded in span streaming mode (additional location)
In the span streaming path, when a StreamedSpan receives an HTTP response, only a simplified 'error'/'ok' status is set (lines 290-294). The actual HTTP status code is never recorded as an attribute. In contrast, the legacy Span path calls `set_http_status()` which records the status code as `http.response.status_code`. This results in loss of HTTP status code telemetry data when using the new span streaming mode.
Check warning on line 177 in sentry_sdk/integrations/stdlib.py
github-actions / warden: find-bugs
[7W8-KHM] HTTP status code not recorded in span streaming mode (additional location)
In the span streaming path, when a StreamedSpan receives an HTTP response, only a simplified 'error'/'ok' status is set (lines 290-294). The actual HTTP status code is never recorded as an attribute. In contrast, the legacy Span path calls `set_http_status()` which records the status code as `http.response.status_code`. This results in loss of HTTP status code telemetry data when using the new span streaming mode.
Check warning on line 158 in sentry_sdk/integrations/redis/_async_common.py
github-actions / warden: find-bugs
Missing exception handling causes spans to never close on Redis errors
In `_sentry_execute_command`, `db_span.__enter__()` and `cache_span.__enter__()` are called, but there's no try/finally block wrapping `await old_execute_command(...)`. If the Redis command throws an exception, the spans' `__exit__` methods will never be called. This causes spans to leak on the scope (corrupting parent-child relationships for subsequent spans) and prevents the spans from being sent to Sentry. The sync version correctly uses try/finally (see `_sync_common.py` lines 151-160).
Check warning on line 158 in sentry_sdk/integrations/redis/_sync_common.py
github-actions / warden: find-bugs
[BXJ-HEW] Missing exception handling causes spans to never close on Redis errors (additional location)
In `_sentry_execute_command`, `db_span.__enter__()` and `cache_span.__enter__()` are called, but there's no try/finally block wrapping `await old_execute_command(...)`. If the Redis command throws an exception, the spans' `__exit__` methods will never be called. This causes spans to leak on the scope (corrupting parent-child relationships for subsequent spans) and prevents the spans from being sent to Sentry. The sync version correctly uses try/finally (see `_sync_common.py` lines 151-160).
Check warning on line 226 in sentry_sdk/integrations/strawberry.py
github-actions / warden: find-bugs
AttributeError when NoOpStreamedSpan._segment is accessed
When span streaming is enabled but the span is either ignored or not sampled, `sentry_sdk.traces.start_span()` returns a `NoOpStreamedSpan` which sets `_segment = None`. The code at line 226 does `isinstance(self.graphql_span, StreamedSpan)` check which passes for `NoOpStreamedSpan` (it's a subclass), then accesses `self.graphql_span._segment.set_attribute(...)`. Since `_segment` is `None` for `NoOpStreamedSpan`, this raises `AttributeError: 'NoneType' object has no attribute 'set_attribute'`. This will crash the GraphQL request handling when a span is unsampled or ignored.
Check warning on line 1292 in sentry_sdk/scope.py
github-actions / warden: find-bugs
[TB2-LGN] AttributeError when NoOpStreamedSpan._segment is accessed (additional location)
When span streaming is enabled but the span is either ignored or not sampled, `sentry_sdk.traces.start_span()` returns a `NoOpStreamedSpan` which sets `_segment = None`. The code at line 226 does `isinstance(self.graphql_span, StreamedSpan)` check which passes for `NoOpStreamedSpan` (it's a subclass), then accesses `self.graphql_span._segment.set_attribute(...)`. Since `_segment` is `None` for `NoOpStreamedSpan`, this raises `AttributeError: 'NoneType' object has no attribute 'set_attribute'`. This will crash the GraphQL request handling when a span is unsampled or ignored.
Check warning on line 298 in sentry_sdk/traces.py
github-actions / warden: find-bugs
Duplicate timestamp initialization causes inconsistent timing
The `__init__` method initializes `_start_timestamp`, `_timestamp`, and `_start_timestamp_monotonic_ns` twice (lines 280-288 and 290-298 are identical). This is a copy-paste bug that causes `_start_timestamp` and `_start_timestamp_monotonic_ns` to be set twice with slightly different values, as time passes between the two calls. The second assignment overwrites the first, making span start times slightly later than they should be.
Check warning on line 631 in sentry_sdk/traces.py
github-actions / warden: find-bugs
NoOpStreamedSpan records lost events multiple times when end() is called more than once
The `NoOpStreamedSpan._end()` method calls `record_lost_event()` unconditionally without any guard to prevent duplicate execution. Unlike `StreamedSpan._end()` which checks `if self._timestamp is not None: return`, `NoOpStreamedSpan` has no such protection. This causes inaccurate lost event counting when: (1) `end()` is called explicitly and the span is also used as a context manager (both `end()` and `__exit__` trigger `_end()`), or (2) `end()` is called multiple times.