Skip to content

fix(nextjs): Make request data available to tracesSampler for edge middleware root spans#22232

Open
s1gr1d wants to merge 4 commits into
developfrom
sig/nextjs-edge-middleware
Open

fix(nextjs): Make request data available to tracesSampler for edge middleware root spans#22232
s1gr1d wants to merge 4 commits into
developfrom
sig/nextjs-edge-middleware

Conversation

@s1gr1d

@s1gr1d s1gr1d commented Jul 13, 2026

Copy link
Copy Markdown
Member

On the edge runtime, Next.js's OTel instrumentation creates and samples the Middleware.execute root span before the Sentry middleware wrapper runs. So normalizedRequest was never on the isolation scope at sampling time, and tracesSampler received undefined.

This adds a beforeSampling hook in the edge SDK which populates normalizedRequest (method, URL, query string) based on HTTP span attributes. It also extends the edge spanStart handler to fork the isolation scope for Middleware.execute root spans (similar to what #22013 did on the Node side after vercel/next.js#95357 made middleware a detached root span).

The isolation-scope fork is extracted into a shared maybeForkIsolationScopeForRootSpan utility used by both the Node and edge handlers.

Fixes #22200

@s1gr1d
s1gr1d requested a review from a team as a code owner July 13, 2026 15:42
@s1gr1d
s1gr1d requested review from chargome, mydea and nicohrubec and removed request for a team July 13, 2026 15:42
Comment thread packages/nextjs/src/edge/index.ts Outdated

@chargome chargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add one (unskipped) test case to verify the intent of this pr works correctly e2e in a non-cf scenario?

@github-actions

Copy link
Copy Markdown
Contributor

👋 @mydea, @nicohrubec — Please review this PR when you get a chance!

Comment thread packages/nextjs/src/edge/index.ts
Comment on lines +110 to +112
if (normalizedRequest) {
getIsolationScope().setSDKProcessingMetadata({ normalizedRequest });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: In serverless warm starts, stale normalizedRequest data from a previous request can persist if a new request lacks HTTP attributes, leading to incorrect sampling decisions.
Severity: MEDIUM

Suggested Fix

The beforeSampling hook should always write to the isolation scope, even if getNormalizedRequestFromAttributes returns undefined. This will ensure that any stale normalizedRequest from a previous invocation is cleared. Modify the logic to call getIsolationScope().setSDKProcessingMetadata({ normalizedRequest }) outside of the if (normalizedRequest) guard, effectively overwriting the old value with the new one (or undefined).

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/nextjs/src/edge/index.ts#L110-L112

Potential issue: In serverless environments with warm starts, the `beforeSampling` hook
only updates the `normalizedRequest` in the isolation scope if the current request span
has HTTP attributes. If a subsequent request lacks these attributes, the `if
(normalizedRequest)` check fails, and the stale `normalizedRequest` from the previous
invocation is not cleared because `setSDKProcessingMetadata` performs a merge, not a
replacement. When `maybeForkIsolationScopeForRootSpan` later clones the isolation scope,
it inherits this stale data, causing the `tracesSampler` to make incorrect sampling
decisions based on data from a previous request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Next.js middleware: normalizedRequest never reaches tracesSampler on the edge runtime, so #21833 has no effect there

2 participants