Repository navigation
Nuxt: share the server-utils nitroIntegration for cache/storage (attempted & reverted — details + how to resume) #24899
Description
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).nitroServerTimingIntegration— setsServer-Timingtrace-propagation headers.
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.
What we tried (closed PRs #24869, #24880)
- Enable
tracingChannel: truein the Nuxt module on Nitro 3 (via anitro:confighook). - Add
nitroIntegrationas an explicit default in the Nuxt server SDK (addIntegrationafter init). - Remove Nuxt's own Nitro-3 storage instrumentation (
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, 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
captureErrorHookproduces cause-chained mechanisms (type: 'chained',exception_id/parent_id/source) andauto.function.nuxt.nitro. - Nuxt filters
HTTPError3xx/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.
- Failing:
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 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.
What shipped instead
- Kept ref(nitro): Move channel-based instrumentation into @sentry/server-utils #24861 (the move) — it stands on its own for
@sentry/nitro, node/deno/bun, and Cloudflare Nitro apps, which do enabletracingChannel. - Kept the Cloudflare orchestrion anchor fix (both integrations anchored on
h3), which is independent of Nuxt. - Reverted the Nuxt adoption (fix(nuxt): Stop double-instrumenting storage on Nitro 3 #24869, fix(nuxt): Remove redundant Nitro 3 route-name plugin #24880 closed); Nuxt keeps its own instrumentation.
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:
- Isolate the unstorage channel. Investigate whether Nitro can enable only
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.) - Make
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. - Reconcile the conflicts if we do want full adoption:
- Error:
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. - Spans: avoid double HTTP/middleware spans when the framework already instruments them.
- Propagation:
Server-Timingmust respect Nuxt's cache-aware propagation (no headers on cached/pre-rendered pages) so it doesn't reintroduce stale-trace-bleed.
- Error:
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
- Move PR: ref(nitro): Move channel-based instrumentation into @sentry/server-utils #24861
- Reverted attempt: fix(nuxt): Stop double-instrumenting storage on Nitro 3 #24869 (storage strip +
tracingChannel+ NuxtnitroIntegrationdefault), fix(nuxt): Remove redundant Nitro 3 route-name plugin #24880 (route-name strip) - e2e:
dev-packages/e2e-tests/test-applications/nuxt-5(cache,storage,errors.server,middleware,tracing,tracing.cached-html) - Relevant code:
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
Activity
linear-code commentedon Sep 30, 2026
JS-3857