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
Follow-up from the nitro → @sentry/server-utils move (#24861). We attempted to have @sentry/nuxt adopt the shared nitroIntegration instead of its own bespoke Nitro instrumentation, but reverted it because it degrades Nuxt's telemetry. This issue records what we did, why it failed, and how to pick it up later.
Background
#24861 moved Nitro's node:diagnostics_channel-based instrumentation into @sentry/server-utils as two integrations:
nitroIntegration — h3/srvx HTTP + middleware spans and unstorage cache spans (subscribes to Nitro's native tracing channels).
Nitro's tracing channels (h3.request, srvx.request, unstorage.*) are opt-in via the Nitro config tracingChannel: true. Only @sentry/nitro's withSentryConfig sets it (packages/nitro/src/config.ts). Nuxt never enables it, so nitroIntegration is inert under Nuxt — its channels never fire.
Enable tracingChannel: true in the Nuxt module on Nitro 3 (via a nitro:config hook).
Add nitroIntegration as an explicit default in the Nuxt server SDK (addIntegration after init).
Remove Nuxt's own Nitro-3 storage instrumentation (storage.server/instrumentStorage) and route-name plugin (update-route-name.server), relying on nitroIntegration + route-detector instead.
Why it failed
The original premise was wrong: there was no double-instrumentation to fix. Because Nuxt never enabled tracingChannel, nitroIntegration produced zero spans under Nuxt; Nuxt's own instrumentStorage was the only source. Removing it broke storage instrumentation (0 spans).
Enabling tracingChannel to fix that turns on the h3/srvx and unstorage channels together, so nitroIntegration then collides with Nuxt's own, more-tailored instrumentation. The nuxt-5 e2e surfaced three conflict classes — two are genuine regressions, not test churn:
1. Error capture (regression).nitroIntegration's h3 captureError grabs errors first with mechanism auto.http.nitro.onTraceError, losing Nuxt's richer capture:
Nuxt's captureErrorHook produces cause-chained mechanisms (type: 'chained', exception_id/parent_id/source) and auto.function.nuxt.nitro.
Nuxt filters HTTPError 3xx/4xx; the h3 channel does not, so it captures errors Nuxt deliberately handles differently.
Failing: tests/errors.server.test.ts (3 cases).
2. Duplicated HTTP/middleware spans.nitroIntegration adds auto.http.nitro.srvx/h3 request + middleware spans on top of Nuxt's existing middleware spans and re-parents the tree (middleware test expected 8 spans, got 14).
Failing: tests/middleware.test.ts (span count + parent-child), tests/cache.test.ts (duplicate-detection test timeout).
3. Trace-propagation conflict (regression).nitroServerTimingIntegration's Server-Timing headers connect pageload↔server traces in addition to Nuxt's <meta> tags — including on cached/pre-rendered pages where Nuxt deliberately omits propagation to avoid stale-trace-bleed. A pre-rendered page that must NOT distribute the trace now does, and pageload parent_span_id no longer matches the span Nuxt's meta logic points at.
Net: Nuxt's instrumentation is intentionally richer/more careful (cause-chained errors, HTTPError filtering, cache-aware meta-tag propagation). Forcing it onto the generic nitroIntegration makes all three worse, for essentially no upside — Nuxt's instrumentStorage and server-utils' captureStorageEvents are near-identical siblings, so the only benefit would be code de-duplication.
Adopting the shared instrumentation under Nuxt is only worthwhile if we can take the cache/storage part without dragging in the HTTP/error/propagation behavior. Options, roughly in order of preference:
Isolate the unstorage channel. Investigate whether Nitro can enable only unstorage.* tracing without h3/srvx (e.g. tracingChannel: { h3: false, srvx: false } while unstorage still traces). If so, Nuxt could enable just that and adopt captureStorageEvents, leaving its own HTTP/error/propagation untouched. (Needs Nitro-side confirmation — unclear whether unstorage tracing is gated by the same flag.)
Make nitroIntegration sub-behaviors toggleable. e.g. nitroIntegration({ http: false, cache: true }) and register nitroServerTimingIntegration separately, so a framework that already owns HTTP/error/propagation can opt into cache-only.
Reconcile the conflicts if we do want full adoption:
Error: nitroIntegration's h3 captureError must not override a framework error hook that owns capture (defer / disable when Nuxt's captureErrorHook is present), and must apply the same HTTPError 3xx/4xx filtering.
Spans: avoid double HTTP/middleware spans when the framework already instruments them.
Propagation: Server-Timing must respect Nuxt's cache-aware propagation (no headers on cached/pre-rendered pages) so it doesn't reintroduce stale-trace-bleed.
Given the low upside (near-identical code) and high reconciliation cost, this is probably only worth doing if option 1 (unstorage isolation) turns out to be clean.
Follow-up from the nitro →
@sentry/server-utilsmove (#24861). We attempted to have@sentry/nuxtadopt the sharednitroIntegrationinstead of its own bespoke Nitro instrumentation, but reverted it because it degrades Nuxt's telemetry. This issue records what we did, why it failed, and how to pick it up later.Background
#24861 moved Nitro's
node:diagnostics_channel-based instrumentation into@sentry/server-utilsas two integrations:nitroIntegration— h3/srvx HTTP + middleware spans and unstorage cache spans (subscribes to Nitro's native tracing channels).nitroServerTimingIntegration— setsServer-Timingtrace-propagation headers.Nitro's tracing channels (
h3.request,srvx.request,unstorage.*) are opt-in via the Nitro configtracingChannel: true. Only@sentry/nitro'swithSentryConfigsets it (packages/nitro/src/config.ts). Nuxt never enables it, sonitroIntegrationis inert under Nuxt — its channels never fire.What we tried (closed PRs #24869, #24880)
tracingChannel: truein the Nuxt module on Nitro 3 (via anitro:confighook).nitroIntegrationas an explicit default in the Nuxt server SDK (addIntegrationafter init).storage.server/instrumentStorage) and route-name plugin (update-route-name.server), relying onnitroIntegration+route-detectorinstead.Why it failed
The original premise was wrong: there was no double-instrumentation to fix. Because Nuxt never enabled
tracingChannel,nitroIntegrationproduced zero spans under Nuxt; Nuxt's owninstrumentStoragewas the only source. Removing it broke storage instrumentation (0 spans).Enabling
tracingChannelto fix that turns on the h3/srvx and unstorage channels together, sonitroIntegrationthen collides with Nuxt's own, more-tailored instrumentation. The nuxt-5 e2e surfaced three conflict classes — two are genuine regressions, not test churn:1. Error capture (regression).
nitroIntegration's h3captureErrorgrabs errors first with mechanismauto.http.nitro.onTraceError, losing Nuxt's richer capture:captureErrorHookproduces cause-chained mechanisms (type: 'chained',exception_id/parent_id/source) andauto.function.nuxt.nitro.HTTPError3xx/4xx; the h3 channel does not, so it captures errors Nuxt deliberately handles differently.tests/errors.server.test.ts(3 cases).2. Duplicated HTTP/middleware spans.
nitroIntegrationaddsauto.http.nitro.srvx/h3request + middleware spans on top of Nuxt's existing middleware spans and re-parents the tree (middleware test expected 8 spans, got 14).tests/middleware.test.ts(span count + parent-child),tests/cache.test.ts(duplicate-detection test timeout).3. Trace-propagation conflict (regression).
nitroServerTimingIntegration'sServer-Timingheaders connect pageload↔server traces in addition to Nuxt's<meta>tags — including on cached/pre-rendered pages where Nuxt deliberately omits propagation to avoid stale-trace-bleed. A pre-rendered page that must NOT distribute the trace now does, and pageloadparent_span_idno longer matches the span Nuxt's meta logic points at.tests/tracing.cached-html.test.ts(4 cases),tests/tracing.test.ts.Net: Nuxt's instrumentation is intentionally richer/more careful (cause-chained errors, HTTPError filtering, cache-aware meta-tag propagation). Forcing it onto the generic
nitroIntegrationmakes all three worse, for essentially no upside — Nuxt'sinstrumentStorageand server-utils'captureStorageEventsare near-identical siblings, so the only benefit would be code de-duplication.What shipped instead
@sentry/nitro, node/deno/bun, and Cloudflare Nitro apps, which do enabletracingChannel.h3), which is independent of Nuxt.How to resume this later
Adopting the shared instrumentation under Nuxt is only worthwhile if we can take the cache/storage part without dragging in the HTTP/error/propagation behavior. Options, roughly in order of preference:
unstorage.*tracing withouth3/srvx(e.g.tracingChannel: { h3: false, srvx: false }while unstorage still traces). If so, Nuxt could enable just that and adoptcaptureStorageEvents, leaving its own HTTP/error/propagation untouched. (Needs Nitro-side confirmation — unclear whether unstorage tracing is gated by the same flag.)nitroIntegrationsub-behaviors toggleable. e.g.nitroIntegration({ http: false, cache: true })and registernitroServerTimingIntegrationseparately, so a framework that already owns HTTP/error/propagation can opt into cache-only.nitroIntegration's h3captureErrormust not override a framework error hook that owns capture (defer / disable when Nuxt'scaptureErrorHookis present), and must apply the same HTTPError 3xx/4xx filtering.Server-Timingmust respect Nuxt's cache-aware propagation (no headers on cached/pre-rendered pages) so it doesn't reintroduce stale-trace-bleed.Given the low upside (near-identical code) and high reconciliation cost, this is probably only worth doing if option 1 (unstorage isolation) turns out to be clean.
Reproduction / references
tracingChannel+ NuxtnitroIntegrationdefault), fix(nuxt): Remove redundant Nitro 3 route-name plugin #24880 (route-name strip)dev-packages/e2e-tests/test-applications/nuxt-5(cache,storage,errors.server,middleware,tracing,tracing.cached-html)packages/nuxt/src/module.ts,packages/nuxt/src/server/sdk.ts,packages/nuxt/src/runtime/utils/instrumentStorage.ts,packages/nuxt/src/runtime/hooks/captureErrorHook.ts,packages/server-utils/src/integrations/nitro/*,packages/nitro/src/config.ts