Skip to content

fix(browser): Clean up pageload readystatechange listener#21632

Draft
andreiborza wants to merge 1 commit into
developfrom
fix/browser-tracing-readystatechange-listener-leak
Draft

fix(browser): Clean up pageload readystatechange listener#21632
andreiborza wants to merge 1 commit into
developfrom
fix/browser-tracing-readystatechange-listener-leak

Conversation

@andreiborza

Copy link
Copy Markdown
Member

The auto-finish readystatechange listener registered for each pageload span in browserTracingIntegration was never removed. Since the handler closes over the idleSpan (and the rest of the route-span closure), every pageload leaked a listener and kept the span it retained alive — the growing set of Sentry functions reported in #21630.

The previous handler was an anonymous arrow function, so it could not be removed via removeEventListener. The { once: true } option suggested in the issue does not fully address the leak either: in the common case where the document is already loaded when the span starts, the listener never fires and therefore is never auto-removed.

This change passes emitFinish directly as the handler and removes it as soon as the auto-finish signal is emitted. This covers both cases:

  • Document already loaded when the span starts → signal emitted synchronously, listener removed immediately.
  • Document still loading → listener fires on the next readystatechange, emits, and removes itself.

Since the framework integrations (Vue, Astro, Next.js, Remix, SvelteKit, Ember) all wrap the browser package's browserTracingIntegration, this single fix covers them too.

Root cause

optionalWindowDocument.addEventListener('readystatechange', () => { emitFinish(); }) added a fresh, unremovable listener for every pageload span without ever detaching it.

Fixes #21630

The auto-finish `readystatechange` listener registered for each pageload
span was never removed. Because it closes over the `idleSpan` (and the
rest of the route-span closure), every pageload leaked a listener and the
span it retained. The previous anonymous handler could not be removed, and
the suggested `{ once: true }` would not help in the common case where the
document is already loaded when the span starts, since the listener never
fires.

Pass `emitFinish` directly as the handler and remove it as soon as the
auto-finish signal is emitted, covering both the already-loaded and
load-later cases.

Fixes #21630

Co-Authored-By: Opus 4.8 <noreply@anthropic.com>
@andreiborza andreiborza requested a review from a team as a code owner June 18, 2026 11:55
@andreiborza andreiborza requested review from Lms24 and logaretm and removed request for a team, Lms24 and logaretm June 18, 2026 11:55
@andreiborza andreiborza marked this pull request as draft June 18, 2026 12:01
@github-actions

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 27.45 kB - -
@sentry/browser - with treeshaking flags 25.88 kB - -
@sentry/browser (incl. Tracing) 45.89 kB +0.03% +12 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 48.12 kB +0.03% +11 B 🔺
@sentry/browser (incl. Tracing, Profiling) 50.67 kB +0.03% +14 B 🔺
@sentry/browser (incl. Tracing, Replay) 85.08 kB +0.01% +4 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 74.69 kB +0.01% +2 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 89.78 kB +0.01% +6 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 102.45 kB +0.01% +7 B 🔺
@sentry/browser (incl. Feedback) 44.62 kB - -
@sentry/browser (incl. sendFeedback) 32.25 kB - -
@sentry/browser (incl. FeedbackAsync) 37.38 kB - -
@sentry/browser (incl. Metrics) 28.52 kB - -
@sentry/browser (incl. Logs) 28.76 kB - -
@sentry/browser (incl. Metrics & Logs) 29.45 kB - -
@sentry/react 29.25 kB - -
@sentry/react (incl. Tracing) 48.18 kB +0.02% +5 B 🔺
@sentry/vue 32.56 kB - -
@sentry/vue (incl. Tracing) 47.76 kB +0.03% +10 B 🔺
@sentry/svelte 27.48 kB - -
CDN Bundle 29.86 kB - -
CDN Bundle (incl. Tracing) 48.29 kB +0.03% +11 B 🔺
CDN Bundle (incl. Logs, Metrics) 31.4 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 49.59 kB +0.03% +11 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 70.71 kB - -
CDN Bundle (incl. Tracing, Replay) 85.62 kB +0.01% +5 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.88 kB +0.01% +5 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 91.46 kB +0.01% +5 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.71 kB +0.01% +8 B 🔺
CDN Bundle - uncompressed 88.8 kB - -
CDN Bundle (incl. Tracing) - uncompressed 146.08 kB +0.03% +38 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 93.5 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 150.06 kB +0.03% +38 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 218.33 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 264.95 kB +0.02% +38 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 268.91 kB +0.02% +38 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 278.65 kB +0.02% +38 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 282.6 kB +0.02% +38 B 🔺
@sentry/nextjs (client) 50.58 kB +0.01% +2 B 🔺
@sentry/sveltekit (client) 46.27 kB +0.02% +9 B 🔺
@sentry/core/server 76.16 kB - -
@sentry/core/browser 63.31 kB - -
@sentry/node-core 61.84 kB -0.01% -1 B 🔽
@sentry/node 127.65 kB -0.01% -1 B 🔽
@sentry/node - without tracing 74.22 kB -0.01% -1 B 🔽
@sentry/aws-serverless 85.33 kB -0.01% -1 B 🔽
@sentry/cloudflare (withSentry) - minified 174.48 kB - -
@sentry/cloudflare (withSentry) 436.52 kB - -

View base workflow run

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.

memory leak in browserTracingIntegration.ts

1 participant