-
Notifications
You must be signed in to change notification settings - Fork 29.8k
fix: trace context propagation from middleware → server #85900
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: canary
Are you sure you want to change the base?
fix: trace context propagation from middleware → server #85900
Conversation
|
Allow CI Workflow Run
Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer |
| finalResponse.headers.set( | ||
| 'x-middleware-override-headers', | ||
| middlewareOverrideHeaders + ',' + overwrittenHeaders.join(',') | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| finalResponse.headers.set( | |
| 'x-middleware-override-headers', | |
| middlewareOverrideHeaders + ',' + overwrittenHeaders.join(',') | |
| ) | |
| const headerValue = middlewareOverrideHeaders | |
| ? middlewareOverrideHeaders + ',' + overwrittenHeaders.join(',') | |
| : overwrittenHeaders.join(',') | |
| finalResponse.headers.set('x-middleware-override-headers', headerValue) |
When setting the x-middleware-override-headers header at line 510, the code concatenates middlewareOverrideHeaders (which can be null) with a string, resulting in the literal string "null" being prepended to the header value. This corrupts the header when trace context is captured but no previous override headers exist.
View Details
Analysis
Null concatenation in x-middleware-override-headers header when trace context is captured without existing overrides
What fails: In packages/next/src/server/web/adapter.ts lines 507-512, when middleware captures trace context but no previous override headers exist, the code concatenates null with string values, resulting in the literal string "null" being prepended to the header value.
How to reproduce:
- Middleware runs and captures trace context (triggers
middlewareTraceContext.length > 0) - No previous middleware override headers exist (
middlewareOverrideHeadersis null fromfinalResponse.headers.get()) - Line 510 executes:
middlewareOverrideHeaders + ',' + overwrittenHeaders.join(',') - Result:
null + ',' + 'traceparent,tracestate'="null,traceparent,tracestate"
Result: The x-middleware-override-headers header contains the literal string "null" as the first element, which corrupts header propagation and may break trace propagation in the server-side code.
Expected: When middlewareOverrideHeaders is null, the header should be set to only the overwritten headers list without the "null" prefix, i.e., "traceparent,tracestate".
Root cause: The refactoring in commit a711b1c ("propagate context in middleware response headers") moved the header update outside the if (middlewareOverrideHeaders) conditional block, but did not add a null check when concatenating the values.
What?
This PR fixes OpenTelemetry trace context propagation when requests pass through Next.js middleware. Previously, middleware traces (edge runtime) and server traces (Node.js runtime) were created as separate, independent traces. Now they are properly connected as parent-child spans within a single unified trace.
Changes:
packages/next/src/server/web/adapter.tsto capture and propagate trace context from middleware spans to server requeststest/e2e/opentelemetry/instrumentation/opentelemetry.test.tsto verify the correct span hierarchyWhy?
When a request goes through middleware in Next.js, two separate runtime environments are involved:
Before this fix, these two environments created disconnected traces:
After the fix:
The Problem:
traceId(if an externaltraceparentheader was provided)traceparent,tracestate) were not being propagated from middleware to the serverHow?
The fix implements W3C Trace Context propagation across the edge → Node.js runtime boundary by leveraging Next.js's existing header propagation mechanism (
x-middleware-request-*headers).