[do not merge] feat: Span streaming & new span API #6804
test-integrations-network.yml
on: pull_request
Matrix: Network
All Network tests passed
4s
Annotations
7 errors and 45 warnings
|
UnboundLocalError when redis command raises exception:
sentry_sdk/integrations/redis/_sync_common.py#L158
When `old_execute_command` raises an exception, the `value` variable is never assigned. The `finally` block then attempts to use `value` in `_set_cache_data(cache_span, self, cache_properties, value)`, which will raise an `UnboundLocalError`. This causes the actual Redis exception to be suppressed and replaced with a confusing internal error.
|
|
StreamedSpan lacks set_status() method causing AttributeError:
sentry_sdk/integrations/sqlalchemy.py#L102
The code calls `span.set_status(SpanStatus.ERROR)` on a `StreamedSpan` instance, but `StreamedSpan` does not have a `set_status()` method - it only has a `status` property setter. This will raise an `AttributeError` at runtime when a SQLAlchemy query errors in streaming mode. Other integrations (e.g., Celery at line 105) correctly use `span.status = SpanStatus.ERROR` instead.
|
|
TypeError when comparing None sample_rand to sample_rate:
sentry_sdk/tracing_utils.py#L1551
When `propagation_context.baggage` exists but doesn't contain a valid `sample_rand` value, `propagation_context.baggage._sample_rand()` returns `None`. This `None` value is then used in the comparison `sampled = sample_rand < sample_rate` at line 1551, which will raise a `TypeError: '<' not supported between instances of 'NoneType' and 'float'`. This occurs when baggage is present but malformed or missing the sample_rand field.
|
|
StreamedSpan lacks set_status() method causing AttributeError on SQL errors:
sentry_sdk/integrations/sqlalchemy.py#L102
The code calls `span.set_status(SpanStatus.ERROR)` on StreamedSpan instances at line 102, but StreamedSpan class in traces.py does not define a `set_status()` method. It only has a `status` property setter. When a SQL error occurs and the span is a StreamedSpan, this will raise an AttributeError, causing the error handler to fail.
|
|
[KE9-DC2] StreamedSpan lacks set_status() method causing AttributeError on SQL errors (additional location):
sentry_sdk/integrations/anthropic.py#L572
The code calls `span.set_status(SpanStatus.ERROR)` on StreamedSpan instances at line 102, but StreamedSpan class in traces.py does not define a `set_status()` method. It only has a `status` property setter. When a SQL error occurs and the span is a StreamedSpan, this will raise an AttributeError, causing the error handler to fail.
|
|
[KE9-DC2] StreamedSpan lacks set_status() method causing AttributeError on SQL errors (additional location):
sentry_sdk/tracing_utils.py#L1124
The code calls `span.set_status(SpanStatus.ERROR)` on StreamedSpan instances at line 102, but StreamedSpan class in traces.py does not define a `set_status()` method. It only has a `status` property setter. When a SQL error occurs and the span is a StreamedSpan, this will raise an AttributeError, causing the error handler to fail.
|
|
TypeError when sample_rand is None in make_sampling_decision:
sentry_sdk/tracing_utils.py#L1551
In `make_sampling_decision()`, when `propagation_context.baggage` exists but `baggage._sample_rand()` returns `None` (which happens when the incoming trace has inconsistent sample_rate and parent_sampled values that cause `_fill_sample_rand()` to fail), the code proceeds to line 1551 where `sampled = sample_rand < sample_rate` raises `TypeError: '<' not supported between instances of 'NoneType' and 'float'`. This can crash the application when handling malformed incoming trace headers from upstream services.
|
|
Network (3.11, ubuntu-22.04)
❌ Patch coverage check failed: 17.19% < target 80%
|
|
Network (3.6, ubuntu-22.04)
❌ Patch coverage check failed: 18.66% < target 80%
|
|
Network (3.6, ubuntu-22.04)
Failed to upload coverage artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.6, ubuntu-22.04)
Failed to upload test artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.8, ubuntu-22.04)
❌ Patch coverage check failed: 17.19% < target 80%
|
|
Network (3.8, ubuntu-22.04)
Failed to upload coverage artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.8, ubuntu-22.04)
Failed to upload test artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.7, ubuntu-22.04)
❌ Patch coverage check failed: 17.19% < target 80%
|
|
Network (3.7, ubuntu-22.04)
Failed to upload coverage artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.7, ubuntu-22.04)
Failed to upload test artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.10, ubuntu-22.04)
❌ Patch coverage check failed: 17.19% < target 80%
|
|
Network (3.10, ubuntu-22.04)
Failed to upload coverage artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.10, ubuntu-22.04)
Failed to upload test artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.12, ubuntu-22.04)
❌ Patch coverage check failed: 17.19% < target 80%
|
|
Network (3.12, ubuntu-22.04)
Failed to upload coverage artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.12, ubuntu-22.04)
Failed to upload test artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.14, ubuntu-22.04)
❌ Patch coverage check failed: 14.83% < target 80%
|
|
Network (3.14, ubuntu-22.04)
Failed to upload coverage artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.14, ubuntu-22.04)
Failed to upload test artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.14t, ubuntu-22.04)
❌ Patch coverage check failed: 14.83% < target 80%
|
|
Network (3.14t, ubuntu-22.04)
Failed to upload coverage artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.14t, ubuntu-22.04)
Failed to upload test artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.9, ubuntu-22.04)
❌ Patch coverage check failed: 18.60% < target 80%
|
|
Network (3.9, ubuntu-22.04)
Failed to upload coverage artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.9, ubuntu-22.04)
Failed to upload test artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.13, ubuntu-22.04)
❌ Patch coverage check failed: 14.83% < target 80%
|
|
Network (3.13, ubuntu-22.04)
Failed to upload coverage artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
Network (3.13, ubuntu-22.04)
Failed to upload test artifact: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run
|
|
StreamedSpan not closed on error in streaming mode:
sentry_sdk/integrations/anthropic.py#L610
When using streaming mode (`_experiments={"trace_lifecycle": "stream"}`), if an exception occurs during the Anthropic API call, the `StreamedSpan` will not have its `__exit__()` method called. The change from `span is not None` to `isinstance(span, Span)` excludes `StreamedSpan` instances from the error cleanup path. Since `_sentry_patched_create_common` uses `get_start_span_function()` which returns `StreamedSpan` in streaming mode, and `span.__enter__()` is called on line 413, the span's cleanup (`_end()`) will never run on error, leaving spans unclosed and scope state corrupted.
|
|
Span streaming mode ignores http_methods_to_capture filter:
sentry_sdk/integrations/asgi.py#L238
In span streaming mode, when the request type is 'http' but the method is not in `http_methods_to_capture`, the code still creates a span (lines 238-240). In contrast, the non-streaming mode correctly sets `transaction = None` and uses `nullcontext()`, creating no span. This causes streaming mode to trace HTTP methods (e.g., OPTIONS, HEAD) that should be filtered out according to the middleware configuration.
|
|
_set_status ignores status parameter for StreamedSpan, always sets ERROR:
sentry_sdk/integrations/celery/__init__.py#L104
The `_set_status` function receives a `status` string parameter ('aborted' or 'internal_error'), but for `StreamedSpan` instances, it ignores this parameter and always sets `SpanStatus.ERROR`. This means that when Celery control flow exceptions (Retry, Ignore, Reject) trigger `_set_status('aborted')`, the span is incorrectly marked as ERROR instead of being handled appropriately. This could result in misleading error metrics in Sentry where task retries are counted as errors.
|
|
HTTP status code attribute not set for StreamedSpan:
sentry_sdk/integrations/httpx.py#L117
The StreamedSpan path only sets `span.status` and `reason` attribute but does not set `SPANDATA.HTTP_STATUS_CODE` (http.response.status_code). The old API via `set_http_status()` sets this attribute, which is used by downstream processing like `http_status_code_to_event_level()` in tracing_utils.py. This results in data parity loss between streaming and non-streaming modes.
|
|
[VXN-676] HTTP status code attribute not set for StreamedSpan (additional location):
sentry_sdk/integrations/httpx.py#L200
The StreamedSpan path only sets `span.status` and `reason` attribute but does not set `SPANDATA.HTTP_STATUS_CODE` (http.response.status_code). The old API via `set_http_status()` sets this attribute, which is used by downstream processing like `http_status_code_to_event_level()` in tracing_utils.py. This results in data parity loss between streaming and non-streaming modes.
|
|
[VXN-676] HTTP status code attribute not set for StreamedSpan (additional location):
sentry_sdk/integrations/stdlib.py#L175
The StreamedSpan path only sets `span.status` and `reason` attribute but does not set `SPANDATA.HTTP_STATUS_CODE` (http.response.status_code). The old API via `set_http_status()` sets this attribute, which is used by downstream processing like `http_status_code_to_event_level()` in tracing_utils.py. This results in data parity loss between streaming and non-streaming modes.
|
|
_end() returns early without setting _finished flag when client is inactive:
sentry_sdk/traces.py#L326
When `client.is_active()` returns False at line 326, the method returns early without setting `self._finished = True`. However, the scope detachment (lines 317-321) and profiler stop (line 314) have already occurred. If `_end()` is called again later when the client becomes active, the method will: (1) try to access the deleted `_context_manager_state` attribute causing a silently-swallowed AttributeError, and (2) attempt to stop an already-stopped profiler. While `capture_internal_exceptions()` prevents crashes, this leads to inconsistent state and harder-to-debug issues.
|
|
NoOpStreamedSpan missing attribute initialization causes AttributeError:
sentry_sdk/traces.py#L539
NoOpStreamedSpan inherits from StreamedSpan but its `__init__` doesn't initialize many attributes that inherited methods/properties access. Properties like `active`, `timestamp`, `start_timestamp`, `status`, and methods like `get_baggage()`, `get_trace_context()`, `dynamic_sampling_context()` will raise `AttributeError` when called because attributes like `_active`, `_timestamp`, `_start_timestamp`, `_status`, `_baggage`, `_parent_span_id` are never set. Any code path using these NoOpStreamedSpan instances that accesses these inherited members will fail at runtime.
|
|
http_methods_to_capture filter bypassed in span streaming mode:
sentry_sdk/integrations/asgi.py#L238
In span streaming mode, when `ty in ("http", "websocket")` but the method is NOT in `http_methods_to_capture`, the code still proceeds to call `sentry_sdk.traces.start_span()` at line 238. In contrast, the legacy path correctly sets `transaction = None` and uses `nullcontext()` to skip span creation. This causes spans to be created for HTTP methods that should be filtered out (e.g., OPTIONS, HEAD when only GET/POST are configured), leading to unwanted telemetry data.
|
|
HTTP status code attribute missing for StreamedSpan in httpx integration:
sentry_sdk/integrations/httpx.py#L116
When span streaming is enabled, the HTTP status code is not recorded as an attribute on the span. The legacy `Span.set_http_status()` method sets both the span status and `SPANDATA.HTTP_STATUS_CODE` (http.response.status_code), but the StreamedSpan path only sets the status and 'reason' attribute. This means spans created in streaming mode will be missing the http.response.status_code attribute, which may affect observability, querying, and dashboards in Sentry that rely on this attribute.
|
|
[RQZ-FEP] HTTP status code attribute missing for StreamedSpan in httpx integration (additional location):
sentry_sdk/integrations/stdlib.py#L175
When span streaming is enabled, the HTTP status code is not recorded as an attribute on the span. The legacy `Span.set_http_status()` method sets both the span status and `SPANDATA.HTTP_STATUS_CODE` (http.response.status_code), but the StreamedSpan path only sets the status and 'reason' attribute. This means spans created in streaming mode will be missing the http.response.status_code attribute, which may affect observability, querying, and dashboards in Sentry that rely on this attribute.
|
|
Async Redis spans leak when command raises exception:
sentry_sdk/integrations/redis/_async_common.py#L145
In `_sentry_execute_command`, both `db_span` and `cache_span` are entered via `__enter__()` but if `await old_execute_command()` raises an exception, the `__exit__()` calls at lines 152-156 (in context after) are never executed. This causes span resource leaks and corrupts the span tree because active spans remain attached to the scope. The synchronous version in `_sync_common.py` correctly uses a `try/finally` block (lines 151-160) to ensure spans are always closed.
|
|
[YBD-DXZ] Async Redis spans leak when command raises exception (additional location):
sentry_sdk/integrations/redis/_sync_common.py#L154
In `_sentry_execute_command`, both `db_span` and `cache_span` are entered via `__enter__()` but if `await old_execute_command()` raises an exception, the `__exit__()` calls at lines 152-156 (in context after) are never executed. This causes span resource leaks and corrupts the span tree because active spans remain attached to the scope. The synchronous version in `_sync_common.py` correctly uses a `try/finally` block (lines 151-160) to ensure spans are always closed.
|
|
[YBD-DXZ] Async Redis spans leak when command raises exception (additional location):
sentry_sdk/_span_batcher.py#L69
In `_sentry_execute_command`, both `db_span` and `cache_span` are entered via `__enter__()` but if `await old_execute_command()` raises an exception, the `__exit__()` calls at lines 152-156 (in context after) are never executed. This causes span resource leaks and corrupts the span tree because active spans remain attached to the scope. The synchronous version in `_sync_common.py` correctly uses a `try/finally` block (lines 151-160) to ensure spans are always closed.
|
|
[YBD-DXZ] Async Redis spans leak when command raises exception (additional location):
sentry_sdk/integrations/redis/_sync_common.py#L158
In `_sentry_execute_command`, both `db_span` and `cache_span` are entered via `__enter__()` but if `await old_execute_command()` raises an exception, the `__exit__()` calls at lines 152-156 (in context after) are never executed. This causes span resource leaks and corrupts the span tree because active spans remain attached to the scope. The synchronous version in `_sync_common.py` correctly uses a `try/finally` block (lines 151-160) to ensure spans are always closed.
|
|
AttributeError when legacy Span is used as parent in start_streamed_span:
sentry_sdk/scope.py#L1297
At line 1240, `parent_span` is assigned from `self.span or self.get_current_scope().span`, which can be a legacy `Span` object (from `sentry_sdk.tracing.Span`). The code at line 1297 accesses `parent_span._segment`, but legacy `Span` does not have a `_segment` attribute. This will raise an `AttributeError` if a legacy span exists on the scope when `start_streamed_span` is called. While the PR states the old and new APIs should not be mixed, there's no runtime guard to provide a helpful error message or graceful degradation.
|
|
NoOpStreamedSpan._end can record duplicate lost events and fail on second call:
sentry_sdk/traces.py#L587
Unlike `StreamedSpan._end()` which has a `_finished` guard, `NoOpStreamedSpan._end()` has no protection against being called multiple times. Each call will record a lost event (lines 596-600), inflating lost event counts. If `_scope` is set, the second call will also try to access `_context_manager_state` which was deleted, causing an AttributeError (caught by `capture_internal_exceptions`).
|
Artifacts
Produced during runtime
| Name | Size | Digest | |
|---|---|---|---|
|
codecov-coverage-results-feat-span-first-test-network
Expired
|
109 KB |
sha256:3addfe4b3450f10c29ea475fa6249c26c5d5aa63a3c7232735263d62370ec295
|
|
|
codecov-test-results-feat-span-first-test-network
Expired
|
232 Bytes |
sha256:bf149dd6e1d9847cb73cc217abd3454b4eef5fa3674acfed0db6aed1b91075ca
|
|