Skip to content

[do not merge] feat: Span streaming & new span API #1102

[do not merge] feat: Span streaming & new span API

[do not merge] feat: Span streaming & new span API #1102

Triggered via pull request February 26, 2026 14:03
@sentrivanasentrivana
synchronize #5551
Status Failure
Total duration 46s
Artifacts

changelog-preview.yml

on: pull_request_target
changelog-preview  /  preview
42s
changelog-preview / preview
Fit to window
Zoom out
Zoom in

Annotations

4 errors and 3 warnings
changelog-preview / preview
Process completed with exit code 1.
StreamedSpan not started before end() in on_operation, causing span to be silently dropped: sentry_sdk/integrations/strawberry.py#L192
In the span streaming path (lines 191-208, 234-235), a StreamedSpan is created via `sentry_sdk.traces.start_span()` but never started (via `.start()` or `with` context manager) before calling `.end()`. The `end()` method calls `__exit__()` which expects `_context_manager_state` to be set by `__enter__()`. Since the span was never entered, `__exit__` fails silently (caught by `capture_internal_exceptions()`), and `_end()` is never called, causing the span to be lost. The same issue affects `on_validate` (lines 242-245, 259-260) and `on_parse` (lines 267-270, 282-283).
[WE6-DJB] StreamedSpan not started before end() in on_operation, causing span to be silently dropped (additional location): sentry_sdk/integrations/stdlib.py#L183
In the span streaming path (lines 191-208, 234-235), a StreamedSpan is created via `sentry_sdk.traces.start_span()` but never started (via `.start()` or `with` context manager) before calling `.end()`. The `end()` method calls `__exit__()` which expects `_context_manager_state` to be set by `__enter__()`. Since the span was never entered, `__exit__` fails silently (caught by `capture_internal_exceptions()`), and `_end()` is never called, causing the span to be lost. The same issue affects `on_validate` (lines 242-245, 259-260) and `on_parse` (lines 267-270, 282-283).
[WE6-DJB] StreamedSpan not started before end() in on_operation, causing span to be silently dropped (additional location): sentry_sdk/integrations/httpx.py#L64
In the span streaming path (lines 191-208, 234-235), a StreamedSpan is created via `sentry_sdk.traces.start_span()` but never started (via `.start()` or `with` context manager) before calling `.end()`. The `end()` method calls `__exit__()` which expects `_context_manager_state` to be set by `__enter__()`. Since the span was never entered, `__exit__` fails silently (caught by `capture_internal_exceptions()`), and `_end()` is never called, causing the span to be lost. The same issue affects `on_validate` (lines 242-245, 259-260) and `on_parse` (lines 267-270, 282-283).
Spans leak when Redis command raises exception in async client: sentry_sdk/integrations/redis/_async_common.py#L135
In `_sentry_execute_command`, if `await old_execute_command()` raises an exception, `db_span.__exit__()` and `cache_span.__exit__()` are never called because there's no try/finally block. This differs from the sync version in `_sync_common.py` which properly wraps the command execution in try/finally (lines 141-150). When Redis operations fail (connection errors, timeouts, etc.), spans will remain unclosed, potentially causing resource leaks or incomplete trace data.
[VWQ-M8X] Spans leak when Redis command raises exception in async client (additional location): sentry_sdk/integrations/redis/_sync_common.py#L148
In `_sentry_execute_command`, if `await old_execute_command()` raises an exception, `db_span.__exit__()` and `cache_span.__exit__()` are never called because there's no try/finally block. This differs from the sync version in `_sync_common.py` which properly wraps the command execution in try/finally (lines 141-150). When Redis operations fail (connection errors, timeouts, etc.), spans will remain unclosed, potentially causing resource leaks or incomplete trace data.
NoOpStreamedSpan.finish() doesn't restore scope state: sentry_sdk/traces.py#L724
The `NoOpStreamedSpan.finish()` method (line 724-725) is overridden to be a no-op (`pass`), but the base class `StreamedSpan.finish()` calls `self.end()` which properly restores the scope's previous span. When a `NoOpStreamedSpan` is created with a scope and used via the `start()`/`finish()` pattern, calling `finish()` will not restore `scope.span` to its previous value, potentially leaving the scope in an inconsistent state where it still references the NoOp span.