feat(browser): Defer fetch span end until stream body resolves#20778
feat(browser): Defer fetch span end until stream body resolves#20778nicohrubec wants to merge 7 commits intodevelopfrom
Conversation
Add a `processSpan` hook alongside the existing event processor to support the span streaming path for fetch stream timestamp corrections. Ref: #20376 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of ending the span at header arrival and patching timestamps later via event processor, defer the span end until the response body fully resolves. Removes the event processor entirely. Closes #20376 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
size-limit report 📦
|
Instead of reimplementing endSpan logic (HTTP status, content-length, onRequestSpanEnd), stash the original handlerData, put the span back in the spans record, and let instrumentFetchRequest handle everything. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a Playwright test verifying that spans produced via the deferred span end path have correct attributes, status, and trace context. Also remove unnecessary dynamic delete from spans record. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove unused span field from deferred data map and rename for clarity. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit dfaaf26. Configure here.
| }, STREAM_RESOLVE_FALLBACK_MS); | ||
|
|
||
| spanIdToFallbackTimeout.set(spanId, fallbackTimeout); | ||
| return; |
There was a problem hiding this comment.
Deferred spans lost in non-streaming transaction path
High Severity
When trackFetchStreamPerformance is enabled without spanStreamingIntegration, the fetch span end is now fully deferred until the body stream resolves. The removed event processor previously ensured the span was always included in the transaction (with the header-arrival timestamp), patching it retroactively if the body resolved in time. Now, if the idle span (pageload/navigation) ends and the transaction is sent before the body resolves, the http.client span will be entirely absent from the transaction — a silent data loss regression compared to the old behavior, which always included the span.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit dfaaf26. Configure here.
There was a problem hiding this comment.
jep right I also ran into this locally. not sure if there is a better way to do this migration though, so we'll likely have to live with this limitation or we'll skip the whole migration altogether so this feature just won't work on the streaming path
There was a problem hiding this comment.
I think this is exactly why this is implemented this way 😅 likely we'll need both ways implemented somehow...


If
trackFetchStreamPerformancewas enabled, the SDK endedhttp.clientspans immediately when response headers arrived, then retroactively patching the end timestamp via an event processor when the transaction is flushed. Now, instead of patching after the fact, the span end is deferred entirely until the response body stream resolves. This removes the need for the event processor and works for both transaction and span streaming paths.Screenshots from a local sample app
Feature disabled:

Feature enabled:

Closes #20376