fix(browser): End the active idle span on pagehide - #23779
Open
logaretm wants to merge 1 commit into
Open
Conversation
A navigation span whose idle timeout has not elapsed when the document
goes away was reaching Sentry as a rootless trace: its children had been
streamed while the page was alive, but the root span never arrived.
`registerBackgroundTabDetection` is meant to be the safety net, but it
listens for `visibilitychange`. Measured on a same-tab cross-document
navigation in Chrome 152, the order is:
pagehide (persisted=true) -> visibilitychange (hidden)
`pagehide` comes first and freezes the document into the bfcache, so a
root span ended on `visibilitychange` is ended on a page that can no
longer send, and it is stranded on its own. That is also why the
cancellation never appears in the debug log: it is logged from a frozen
document.
`pagehide` is the last point at which a document can still send, so the
idle span is ended there instead, which also puts the root in the same
final batch as the remaining children. The trace then arrives whole or
not at all, rather than headless.
Verified against a real project: rootless traces appear without this and
consistently do not with it. Trace delivery volume is unchanged (7-8 of
10 traces over three runs each way), which is expected - this changes
whether the root travels with its children, not how much gets through.
The traces that go missing entirely are a separate problem: four envelope
requests per run are killed with `net::ERR_ABORTED` during unload, which
points at the `keepalive` budget in `fetch.ts` rather than at span
lifecycle.
Contributor
size-limit report 📦
|
logaretm
marked this pull request as ready for review
August 31, 2026 16:16
logaretm
requested review from
Lms24 and
msonnb
and removed request for
a team
August 31, 2026 16:16
Lms24
reviewed
Sep 1, 2026
Lms24
left a comment
Member
There was a problem hiding this comment.
Looks reasonable to me! The interesting part is that the visibilityChange listener that also ends the span is gated by the markBackgroundSpan.
Which tbh, I'm a bit torn if we should keep supporting long-term because to me it seems like we'd always want to cancel and end a span when going to background. It's also a bit weird that the visibilityChange logic sets an error status. But this decision is out of scope of this PR I'd say.
So I'd suggest, for now we also gate on markBackgroundSpan, wdyt?
| WINDOW.addEventListener?.('pagehide', () => { | ||
| const activeSpan = getActiveIdleSpan(client); | ||
| if (activeSpan && !spanToJSON(activeSpan).end_timestamp) { | ||
| activeSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON, 'documentHidden'); |
Member
There was a problem hiding this comment.
super-l: we can use the @sentry/conventions constant here instead
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ends the active idle span on
pagehide, so a navigation span that is still open when the user leaves the page doesn't lose its root.