You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
UI profiling: idle samples are attributed to stack 0 in convertToContinuousProfile #25757
I searched the repo for "idle", "stackId" and "UI profiling" issues and found none.
SDK and version
@sentry/browser 10.76.2. The same code is on develop in packages/browser/src/profiling/utils.ts.
Description
convertToContinuousProfile writes a JS Self-Profiling sample that has no stackId as stack_id: sample.stackId ?? 0. A sample without a stackId means that the thread was idle. Stack 0 is a real stack: the first stack that the profiler recorded in the chunk. So the SDK reports every idle sample in the chunk as time spent in the frames of stack 0.
The legacy converter, convertJSSelfProfileToSampledFormat, handles the same case correctly. It adds one empty stack and points every idle sample at it.
Impact
With profileLifecycle: 'trace', a root span often starts the profiler from inside a timer or event callback. Stack 0 is then rooted at the SDK's own sentryWrapped wrapper (helpers.ts), plus the callback that started the span. A parentless http.client root span that waits 2 s on the network then shows in the flame graph as about 2 s of main-thread time in sentryWrapped. In one of our production chunks, the wrapper frame was in 429 of 430 samples, and no other frame was in more than 1. Because of this, a third-party telemetry request looked as if it blocked the main thread.
Steps to reproduce
We found this by reading the code and checking production profiles. We did not run the steps below in isolation.
Call Sentry.init with browserProfilingIntegration(), profileLifecycle: 'trace', profileSessionSampleRate: 1 and tracesSampleRate: 1. Use Chrome, and serve the page with Document-Policy: js-profiling.
From a setTimeout callback, start a root span and keep it open for 2 s while the page is idle. For example, send an XHR to a slow endpoint while span streaming is on.
Open the continuous profile for that window.
Expected result
Idle samples have an empty stack, as in the legacy format. The flame graph shows the thread as idle.
Actual result
Idle samples point to stack 0. The flame graph shows the callback's stack as busy for the whole window.
Suggested fix
Do what the legacy converter does. On the first idle sample, add one empty stack, and point every idle sample at its index instead of at 0.
Activity
linear-code commentedon Oct 27, 2026
JS-4007