Skip to content

fix(tracing): Skip child span creation in streaming path when no current span (HTTP clients)#6811

Merged
sl0thentr0py merged 19 commits into
masterfrom
ivana/streaming-child-spans-http-clients
Jul 16, 2026
Merged

fix(tracing): Skip child span creation in streaming path when no current span (HTTP clients)#6811
sl0thentr0py merged 19 commits into
masterfrom
ivana/streaming-child-spans-http-clients

Conversation

@sentrivana

Copy link
Copy Markdown
Contributor

Summary

  • When span streaming is enabled and there is no current span, HTTP client integrations should not create new root segments for child-span operations
  • Adds a sentry_sdk.traces.get_current_span() is None guard before creating spans in the streaming path
  • Affected integrations: httpx, httpx2, pyreqwest, boto3, stdlib (urllib)

Test plan

  • Existing tests pass
  • Verify that in streaming mode, no orphan root segments are created for outgoing HTTP requests when there's no active span

🤖 Generated with Claude Code

…ent span (HTTP clients)

When span streaming is enabled and there is no current span,
HTTP client integrations should not create new root segments
for child-span operations. This adds a guard check using
`sentry_sdk.traces.get_current_span()` before creating spans
in the streaming path.

Affected integrations: httpx, httpx2, pyreqwest, boto3, stdlib.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread sentry_sdk/integrations/httpx2.py
Comment thread sentry_sdk/integrations/httpx.py
…an guards

httpx2: Wrap HTTP calls in parent span context so child spans are created.
strawberry: Remove async/sync branching since both paths now produce 5 spans.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Codecov Results 📊

91854 passed | ⏭️ 6302 skipped | Total: 98156 | Pass Rate: 93.58% | Execution Time: 317m 25s

📊 Comparison with Base Branch

Metric Change
Total Tests 📈 +1205
Passed Tests 📈 +1217
Failed Tests 📉 -28
Skipped Tests 📈 +16

➖ Removed Tests (28)

View removed tests
  • test_job_retry[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_job_retry[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq]
    • File: tests.integrations.arq.test_arq
  • test_span_origin_consumer[True-init_arq_with_dict_settings]
    • File: tests.integrations.arq.test_arq

All tests are passing successfully.

✅ Patch coverage is 91.38%. Project has 2476 uncovered lines.
✅ Project coverage is 89.69%. Comparing base (base) to head (head).

Files with missing lines (4)
File Patch % Lines
sentry_sdk/integrations/stdlib.py 86.67% ⚠️ 2 Missing and 2 partials
sentry_sdk/tracing_utils.py 81.82% ⚠️ 2 Missing and 1 partials
sentry_sdk/integrations/boto3.py 50.00% ⚠️ 1 Missing and 1 partials
sentry_sdk/integrations/pyreqwest.py 100.00% ⚠️ 2 partials
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
+ Coverage    89.22%    89.69%    +0.47%
==========================================
  Files          193       193         —
  Lines        24011     24004        -7
  Branches      8360      8340       -20
==========================================
+ Hits         21421     21528      +107
- Misses        2590      2476      -114
- Partials      1379      1385        +6

Generated by Codecov Action

ericapisani and others added 2 commits July 14, 2026 10:42
The streaming child-span guard added for HTTP client integrations skips
child-span creation when there is no current span. The httpx2 and
strawberry tests were updated for this, but the equivalent httpx
streaming tests were missed, so no HTTP client span was produced and
the tests failed with StopIteration.

Wrap each streaming test's request in a parent span so a child HTTP
client span is created, mirroring the httpx2 test updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@ericapisani ericapisani left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking because we're losing trace propagation within some integrations with these changes. Will work on fixes now

…nt span (HTTP clients)

Previously, when span streaming was enabled and there was no active
span, httpx/httpx2/httplib skipped trace header propagation entirely
by returning early. This diverged from the legacy behavior and dropped
distributed tracing continuity for outgoing requests made outside of
a span.

Extract the shared header-propagation logic into
`propagate_trace_headers()` in tracing_utils.py and call it from the
no-current-span early-return branches so headers are still attached
from the scope's propagation context.
@ericapisani ericapisani marked this pull request as ready for review July 15, 2026 16:45
@ericapisani ericapisani requested a review from a team as a code owner July 15, 2026 16:45
@ericapisani ericapisani dismissed their stale review July 15, 2026 16:46

I've made changes to propagate traces

Comment thread sentry_sdk/integrations/httpx.py
The httpx sync and async streaming paths returned early without
propagating trace headers when there was no active span, dropping
distributed tracing continuity for outgoing requests made outside of
a span. Call propagate_trace_headers() in both no-current-span
branches so headers are attached from the scope's propagation context,
matching httpx2 and httplib.

Add sync and async tests covering trace propagation with span
streaming enabled and no active span.
Comment thread sentry_sdk/integrations/stdlib.py
Comment on lines +60 to +61
if sentry_sdk.traces.get_current_span() is None:
propagate_trace_headers(client, request)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The httpx integration fails to propagate trace headers when no active span exists and streaming is enabled, unlike the httpx2 integration.
Severity: MEDIUM

Suggested Fix

Add a call to propagate_trace_headers(client, request) before the early-return statements in the synchronous and asynchronous send methods in sentry_sdk/integrations/httpx.py. This will make its behavior consistent with the httpx2.py implementation.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: sentry_sdk/integrations/httpx.py#L60-L61

Potential issue: In `sentry_sdk/integrations/httpx.py`, both the synchronous `send` and
asynchronous `send` methods do not propagate trace headers when returning early if no
active span exists and span streaming is enabled. Specifically,
`propagate_trace_headers(client, request)` is not called before the early return. This
behavior is inconsistent with the `httpx2.py` integration, which handles this case
correctly. The consequence is that distributed tracing is broken for `httpx` requests
made outside of an active transaction when streaming is used.

Also affects:

  • sentry_sdk/integrations/httpx.py:145~146

Did we get this right? 👍 / 👎 to inform future reviews.

Comment thread sentry_sdk/tracing_utils.py
Comment thread sentry_sdk/integrations/pyreqwest.py Outdated
sentrivana and others added 5 commits July 16, 2026 13:38
…ent span (HTTP clients)

When span streaming is enabled and there is no current span,
HTTP client integrations should not create new root segments
for child-span operations. This adds a guard check using
`sentry_sdk.traces.get_current_span()` before creating spans
in the streaming path.

Affected integrations: httpx, httpx2, pyreqwest, boto3, stdlib.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…an guards

httpx2: Wrap HTTP calls in parent span context so child spans are created.
strawberry: Remove async/sync branching since both paths now produce 5 spans.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The streaming child-span guard added for HTTP client integrations skips
child-span creation when there is no current span. The httpx2 and
strawberry tests were updated for this, but the equivalent httpx
streaming tests were missed, so no HTTP client span was produced and
the tests failed with StopIteration.

Wrap each streaming test's request in a parent span so a child HTTP
client span is created, mirroring the httpx2 test updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nt span (HTTP clients)

Previously, when span streaming was enabled and there was no active
span, httpx/httpx2/httplib skipped trace header propagation entirely
by returning early. This diverged from the legacy behavior and dropped
distributed tracing continuity for outgoing requests made outside of
a span.

Extract the shared header-propagation logic into
`propagate_trace_headers()` in tracing_utils.py and call it from the
no-current-span early-return branches so headers are still attached
from the scope's propagation context.
The httpx sync and async streaming paths returned early without
propagating trace headers when there was no active span, dropping
distributed tracing continuity for outgoing requests made outside of
a span. Call propagate_trace_headers() in both no-current-span
branches so headers are attached from the scope's propagation context,
matching httpx2 and httplib.

Add sync and async tests covering trace propagation with span
streaming enabled and no active span.
@sl0thentr0py sl0thentr0py force-pushed the ivana/streaming-child-spans-http-clients branch from c8c9924 to 2b59942 Compare July 16, 2026 11:38
@sl0thentr0py sl0thentr0py enabled auto-merge (squash) July 16, 2026 11:48
Comment thread sentry_sdk/integrations/pyreqwest.py
…:getsentry/sentry-python into ivana/streaming-child-spans-http-clients

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 557ecc0. Configure here.

Comment thread sentry_sdk/tracing_utils.py
Comment thread sentry_sdk/integrations/pyreqwest.py Outdated
@sl0thentr0py sl0thentr0py merged commit 5ba869c into master Jul 16, 2026
145 checks passed
@sl0thentr0py sl0thentr0py deleted the ivana/streaming-child-spans-http-clients branch July 16, 2026 12:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants