Skip to content

Commit 0730e09

Browse files
committed
fix(nextjs): Bind wrapper isolation scope to pages-router API transaction
The wrapper forks a fresh isolation scope via withIsolationScope, but the transaction is now the Next.js auto-instrumentation root span, which captured a different scope. Request data, tags, and breadcrumbs set during the handler were therefore lost from the transaction. Bind the wrapper's scope to the active root span so they land on it again.
1 parent 70a154c commit 0730e09

2 files changed

Lines changed: 38 additions & 2 deletions

File tree

packages/nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
import { captureException, debug, httpRequestToRequestData, objectify, withIsolationScope } from '@sentry/core';
1+
import {
2+
captureException,
3+
debug,
4+
getActiveSpan,
5+
getCurrentScope,
6+
getRootSpan,
7+
httpRequestToRequestData,
8+
objectify,
9+
setCapturedScopesOnSpan,
10+
withIsolationScope,
11+
} from '@sentry/core';
212
import type { NextApiRequest } from 'next';
313
import type { AugmentedNextApiResponse, NextApiHandler } from '../types';
414
import { flushSafelyWithTimeout } from '../utils/responseEnd';
@@ -48,6 +58,15 @@ export function wrapApiHandlerWithSentry(apiHandler: NextApiHandler, parameteriz
4858
isolationScope.setSDKProcessingMetadata({ normalizedRequest: httpRequestToRequestData(req) });
4959
isolationScope.setTransactionName(`${reqMethod}${parameterizedRoute}`);
5060

61+
// We no longer create the transaction ourselves: it's the Next.js root span, which captured a different
62+
// isolation scope than the one forked here. Bind this scope to that span so the request data and anything
63+
// set on the scope during the handler (tags, breadcrumbs) land on the transaction.
64+
const activeSpan = getActiveSpan();
65+
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;
66+
if (rootSpan) {
67+
setCapturedScopesOnSpan(rootSpan, getCurrentScope(), isolationScope);
68+
}
69+
5170
try {
5271
return await wrappingTarget.apply(thisArg, args);
5372
} catch (e) {

packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { captureException, winterCGRequestToRequestData, withIsolationScope } from '@sentry/core';
1+
import {
2+
captureException,
3+
getActiveSpan,
4+
getCurrentScope,
5+
getRootSpan,
6+
setCapturedScopesOnSpan,
7+
winterCGRequestToRequestData,
8+
withIsolationScope,
9+
} from '@sentry/core';
210
import { flushSafelyWithTimeout } from '../common/utils/responseEnd';
311
import type { EdgeRouteHandler } from './types';
412

@@ -27,6 +35,15 @@ export function wrapApiHandlerWithSentry<H extends EdgeRouteHandler>(
2735
isolationScope.setTransactionName(`handler (${parameterizedRoute})`);
2836
}
2937

38+
// We no longer create the transaction ourselves: it's the Next.js `Node.runHandler` root span, which
39+
// captured a different isolation scope than the one forked here. Bind this scope to that span so the
40+
// request data and anything set on the scope during the handler (tags, breadcrumbs) land on the transaction.
41+
const activeSpan = getActiveSpan();
42+
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;
43+
if (rootSpan) {
44+
setCapturedScopesOnSpan(rootSpan, getCurrentScope(), isolationScope);
45+
}
46+
3047
try {
3148
return await wrappingTarget.apply(thisArg, args);
3249
} catch (error) {

0 commit comments

Comments
 (0)