Preserve deferred middleware instrumentation spans when the adapter throws (#100552)
### What?
Direct Node.js middleware/proxy entrypoints now emit the deferred
`instrumentation.ts` lifecycle spans (`load instrumentation module`,
`register instrumentation`) even when the adapter rejects the request
before it runs the middleware.
### Why?
Follow-up to #100350. When a request to the Node.js middleware
entrypoint triggers `instrumentation.ts` registration, the lifecycle
spans are deferred and emitted inside the `Middleware.execute` span. If
adapter setup throws first (e.g. the configured cache handler throws
while the adapter builds the request's incremental cache), the emitter
is never called. Registration has already completed, so no later request
receives it either, and those spans are lost for the lifetime of the
instance.
This only affects direct entrypoints (adapters, deployments). `next
start` / `next dev` register instrumentation at startup.
### How?
- The middleware entrypoint template, which owns the deferred emitter,
now awaits the adapter in a `try/catch`. On error it calls the emitter,
then rethrows the original error unchanged. The adapter and its options
are unchanged.
- The emitter already emits at most once, so the fallback does nothing
when the spans were already emitted inside `Middleware.execute` (e.g.
the user's middleware threw). Successful requests keep their existing
span parentage. In the fallback case there is no middleware span, so the
spans are emitted in the caller's active context (or as roots).
- The fallback call is guarded so that a failure while tracing can't
replace the adapter's error.
**Test:** this reuses the existing direct-entrypoint OpenTelemetry
harness (`test/e2e/opentelemetry/instrumentation`, `next start` only).
The harness calls the built middleware entrypoint's `handler` with a
real `Request`, like the Vercel adapter does.
- The fixture's custom cache handler throws from its constructor when a
test-only request header is set. The adapter constructs it per request,
before running the middleware.
- A new suite, in a fresh process, sends that request through the normal
middleware flow and asserts that:
- the original error propagates;
- both lifecycle spans are collected exactly once, with no
`Middleware.execute` span;
- a later valid request succeeds without emitting them again.
- The test fails without the fix.
### Verification
- New test plus the existing "should trace instrumentation registration
in the first middleware request" variants: `pnpm test-start-turbo` and
`pnpm test-start-webpack` on
`test/e2e/opentelemetry/instrumentation/opentelemetry.test.ts`
- `pnpm jest
packages/next/src/server/lib/router-utils/instrumentation-globals.test.ts`
- `pnpm --filter=next types`, `pnpm typescript`
<!-- NEXT_JS_LLM -->
<!-- fleet bb16cbee-5cda-4702-b442-ea49bf648fe0 -->
---------
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>