Skip to content

Commit 366ed0b

Browse files
author
Luca Forstner
authored
fix(nextjs): Don't capture not-found and redirect errors in generation functions (#10057)
1 parent 5eeb8a9 commit 366ed0b

File tree

5 files changed

+84
-11
lines changed

5 files changed

+84
-11
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { notFound } from 'next/navigation';
2+
3+
export const dynamic = 'force-dynamic';
4+
5+
export default function PageWithRedirect() {
6+
return <p>Hello World!</p>;
7+
}
8+
9+
export async function generateMetadata() {
10+
notFound();
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { redirect } from 'next/navigation';
2+
3+
export const dynamic = 'force-dynamic';
4+
5+
export default function PageWithRedirect() {
6+
return <p>Hello World!</p>;
7+
}
8+
9+
export async function generateMetadata() {
10+
redirect('/');
11+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <p>Home</p>;
3+
}

dev-packages/e2e-tests/test-applications/nextjs-14/tests/generation-functions.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,37 @@ test('Should send a transaction and an error event for a faulty generateViewport
7777
expect(await transactionPromise).toBeDefined();
7878
expect(await errorEventPromise).toBeDefined();
7979
});
80+
81+
test('Should send a transaction event with correct status for a generateMetadata() function invokation with redirect()', async ({
82+
page,
83+
}) => {
84+
const testTitle = 'redirect-foobar';
85+
86+
const transactionPromise = waitForTransaction('nextjs-14', async transactionEvent => {
87+
return (
88+
transactionEvent?.transaction === 'Page.generateMetadata (/generation-functions/with-redirect)' &&
89+
transactionEvent.contexts?.trace?.data?.['searchParams']?.['metadataTitle'] === testTitle
90+
);
91+
});
92+
93+
await page.goto(`/generation-functions/with-redirect?metadataTitle=${testTitle}`);
94+
95+
expect((await transactionPromise).contexts?.trace?.status).toBe('ok');
96+
});
97+
98+
test('Should send a transaction event with correct status for a generateMetadata() function invokation with notfound()', async ({
99+
page,
100+
}) => {
101+
const testTitle = 'notfound-foobar';
102+
103+
const transactionPromise = waitForTransaction('nextjs-14', async transactionEvent => {
104+
return (
105+
transactionEvent?.transaction === 'Page.generateMetadata (/generation-functions/with-notfound)' &&
106+
transactionEvent.contexts?.trace?.data?.['searchParams']?.['metadataTitle'] === testTitle
107+
);
108+
});
109+
110+
await page.goto(`/generation-functions/with-notfound?metadataTitle=${testTitle}`);
111+
112+
expect((await transactionPromise).contexts?.trace?.status).toBe('not_found');
113+
});

packages/nextjs/src/common/wrapGenerationFunctionWithSentry.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import {
66
getCurrentScope,
77
handleCallbackErrors,
88
runWithAsyncContext,
9-
startSpan,
9+
startSpanManual,
1010
} from '@sentry/core';
1111
import type { WebFetchHeaders } from '@sentry/types';
1212
import { winterCGHeadersToDict } from '@sentry/utils';
1313

1414
import type { GenerationFunctionContext } from '../common/types';
15+
import { isNotFoundNavigationError, isRedirectNavigationError } from './nextNavigationErrorUtils';
1516
import { commonObjectToPropagationContext } from './utils/commonObjectTracing';
1617

1718
/**
@@ -61,7 +62,7 @@ export function wrapGenerationFunctionWithSentry<F extends (...args: any[]) => a
6162
transactionContext.parentSpanId = commonSpanId;
6263
}
6364

64-
return startSpan(
65+
return startSpanManual(
6566
{
6667
op: 'function.nextjs',
6768
name: `${componentType}.${generationFunctionIdentifier} (${componentRoute})`,
@@ -76,18 +77,31 @@ export function wrapGenerationFunctionWithSentry<F extends (...args: any[]) => a
7677
},
7778
},
7879
},
79-
() => {
80+
span => {
8081
return handleCallbackErrors(
8182
() => originalFunction.apply(thisArg, args),
82-
err =>
83-
captureException(err, {
84-
mechanism: {
85-
handled: false,
86-
data: {
87-
function: 'wrapGenerationFunctionWithSentry',
83+
err => {
84+
if (isNotFoundNavigationError(err)) {
85+
// We don't want to report "not-found"s
86+
span?.setStatus('not_found');
87+
} else if (isRedirectNavigationError(err)) {
88+
// We don't want to report redirects
89+
span?.setStatus('ok');
90+
} else {
91+
span?.setStatus('internal_error');
92+
captureException(err, {
93+
mechanism: {
94+
handled: false,
95+
data: {
96+
function: 'wrapGenerationFunctionWithSentry',
97+
},
8898
},
89-
},
90-
}),
99+
});
100+
}
101+
},
102+
() => {
103+
span?.end();
104+
},
91105
);
92106
},
93107
);

0 commit comments

Comments
 (0)