SDK
@sentry/react 10.70.0 (also present in @sentry/browser framework integrations generally)
Description
browserTracingIntegration deliberately anchors the pageload span to the performance time origin:
// packages/browser/src/tracing/browserTracingIntegration.ts:690-700
if (instrumentPageLoad) {
const origin = browserPerformanceTimeOrigin();
startBrowserTracingPageLoadSpan(client, {
name: WINDOW.location.pathname,
// pageload should always start at timeOrigin (and needs to be in s, not ms)
startTime: origin ? origin / 1000 : undefined,
...
The framework routing integrations start their own pageload span and omit startTime:
// packages/react/src/tanstackrouter.ts:90-99
const pageloadSpan = startBrowserTracingPageLoadSpan(client, {
name: routeMatch ? routeMatch.routeId : initialWindowLocation.pathname,
attributes: { ... },
// no startTime
});
Same omission in packages/react/src/reactrouter-compat-utils/instrumentation.ts (~line 385, covers React Router v6/v7), reactrouter.ts (v4/v5) and reactrouterv3.ts. reactRouterV6/V7BrowserTracingIntegration are affected via the compat helper.
Neither _createRouteSpan nor startIdleSpan in @sentry/core special-cases op: 'pageload', so the span simply starts whenever startIdleSpan is called.
Expected
Pageload span duration measures from navigationStart, matching browserTracingIntegration.
Actual
Pageload span duration measures from whenever Sentry.init runs. Everything before that — DNS, TLS, TTFB, HTML parse, and any bundle chunks loaded before init — is excluded from the transaction duration, while the browser-instrumentation child spans still carry their real absolute timestamps from the Performance API.
The gap is largest for apps that init from a dynamically imported bootstrap chunk, but it is nonzero for every consumer, so pageload P95 is systematically under-reported and is not comparable to the generic integration's numbers.
Suggested fix
Pass the same anchor the generic integration uses wherever a framework integration starts a pageload span:
const origin = browserPerformanceTimeOrigin();
startBrowserTracingPageLoadSpan(client, {
name: ...,
startTime: origin ? origin / 1000 : undefined,
attributes: { ... },
});
Alternatively, default it inside startBrowserTracingPageLoadSpan (or in _createRouteSpan when op === 'pageload') so every caller gets it and the invariant in the existing comment holds in one place.
Notes
Reported from source reading rather than a runtime repro. Possibly related: #23253 (pageload transaction name ignores router basepath).
SDK
@sentry/react10.70.0 (also present in@sentry/browserframework integrations generally)Description
browserTracingIntegrationdeliberately anchors the pageload span to the performance time origin:The framework routing integrations start their own pageload span and omit
startTime:Same omission in
packages/react/src/reactrouter-compat-utils/instrumentation.ts(~line 385, covers React Router v6/v7),reactrouter.ts(v4/v5) andreactrouterv3.ts.reactRouterV6/V7BrowserTracingIntegrationare affected via the compat helper.Neither
_createRouteSpannorstartIdleSpanin@sentry/corespecial-casesop: 'pageload', so the span simply starts wheneverstartIdleSpanis called.Expected
Pageload span duration measures from navigationStart, matching
browserTracingIntegration.Actual
Pageload span duration measures from whenever
Sentry.initruns. Everything before that — DNS, TLS, TTFB, HTML parse, and any bundle chunks loaded beforeinit— is excluded from the transaction duration, while the browser-instrumentation child spans still carry their real absolute timestamps from the Performance API.The gap is largest for apps that
initfrom a dynamically imported bootstrap chunk, but it is nonzero for every consumer, so pageload P95 is systematically under-reported and is not comparable to the generic integration's numbers.Suggested fix
Pass the same anchor the generic integration uses wherever a framework integration starts a pageload span:
Alternatively, default it inside
startBrowserTracingPageLoadSpan(or in_createRouteSpanwhenop === 'pageload') so every caller gets it and the invariant in the existing comment holds in one place.Notes
Reported from source reading rather than a runtime repro. Possibly related: #23253 (pageload transaction name ignores router basepath).