From 432390b07267bb3036e1d6606a250710414dd92e Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 31 Jul 2025 17:19:37 +0200 Subject: [PATCH] fix(browser-utils): Ensure web vital client hooks unsubscribe correctly --- packages/browser-utils/src/metrics/utils.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/browser-utils/src/metrics/utils.ts b/packages/browser-utils/src/metrics/utils.ts index e56d0ee98d42..5caab5bc75cc 100644 --- a/packages/browser-utils/src/metrics/utils.ts +++ b/packages/browser-utils/src/metrics/utils.ts @@ -226,13 +226,21 @@ export function listenForWebVitalReportEvents( // we only want to collect LCP if we actually navigate. Redirects should be ignored. if (!options?.isRedirect) { _runCollectorCallbackOnce('navigation'); - unsubscribeStartNavigation?.(); - unsubscribeAfterStartPageLoadSpan?.(); + safeUnsubscribe(unsubscribeStartNavigation, unsubscribeAfterStartPageLoadSpan); } }); const unsubscribeAfterStartPageLoadSpan = client.on('afterStartPageLoadSpan', span => { pageloadSpanId = span.spanContext().spanId; - unsubscribeAfterStartPageLoadSpan?.(); + safeUnsubscribe(unsubscribeAfterStartPageLoadSpan); }); } + +/** + * Invoke a list of unsubscribers in a safe way, by deferring the invocation to the next tick. + * This is necessary because unsubscribing in sync can lead to other callbacks no longer being invoked + * due to in-place array mutation of the subscribers array on the client. + */ +function safeUnsubscribe(...unsubscribers: (() => void | undefined)[]): void { + unsubscribers.forEach(u => u && setTimeout(u, 0)); +}