Skip to content

Commit 6347301

Browse files
lforstonurtemizkan
authored andcommitted
fix(nextjs): Do not report redirects and notFound calls as errors in server actions (#10474)
1 parent fe096e8 commit 6347301

File tree

3 files changed

+54
-6
lines changed

3 files changed

+54
-6
lines changed

dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-action/page.tsx

+24-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as Sentry from '@sentry/nextjs';
22
import { headers } from 'next/headers';
3+
import { notFound } from 'next/navigation';
34

45
export default function ServerComponent() {
56
async function myServerAction(formData: FormData) {
@@ -14,11 +15,29 @@ export default function ServerComponent() {
1415
);
1516
}
1617

18+
async function notFoundServerAction(formData: FormData) {
19+
'use server';
20+
return await Sentry.withServerActionInstrumentation(
21+
'notFoundServerAction',
22+
{ formData, headers: headers(), recordResponse: true },
23+
() => {
24+
notFound();
25+
},
26+
);
27+
}
28+
1729
return (
18-
// @ts-ignore
19-
<form action={myServerAction}>
20-
<input type="text" defaultValue={'some-default-value'} name="some-text-value" />
21-
<button type="submit">Run Action</button>
22-
</form>
30+
<>
31+
{/* @ts-ignore */}
32+
<form action={myServerAction}>
33+
<input type="text" defaultValue={'some-default-value'} name="some-text-value" />
34+
<button type="submit">Run Action</button>
35+
</form>
36+
{/* @ts-ignore */}
37+
<form action={notFoundServerAction}>
38+
<input type="text" defaultValue={'some-default-value'} name="some-text-value" />
39+
<button type="submit">Run NotFound Action</button>
40+
</form>
41+
</>
2342
);
2443
}

dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts

+16
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,22 @@ test('Should send a transaction for instrumented server actions', async ({ page
140140
expect(Object.keys((await serverComponentTransactionPromise).request?.headers || {}).length).toBeGreaterThan(0);
141141
});
142142

143+
test('Should set not_found status for server actions calling notFound()', async ({ page }) => {
144+
const nextjsVersion = packageJson.dependencies.next;
145+
const nextjsMajor = Number(nextjsVersion.split('.')[0]);
146+
test.skip(!isNaN(nextjsMajor) && nextjsMajor < 14, 'only applies to nextjs apps >= version 14');
147+
148+
const serverComponentTransactionPromise = waitForTransaction('nextjs-13-app-dir', async transactionEvent => {
149+
return transactionEvent?.transaction === 'serverAction/notFoundServerAction';
150+
});
151+
152+
await page.goto('/server-action');
153+
await page.getByText('Run NotFound Action').click();
154+
155+
expect(await serverComponentTransactionPromise).toBeDefined();
156+
expect(await (await serverComponentTransactionPromise).contexts?.trace?.status).toBe('not_found');
157+
});
158+
143159
test('Will not include spans in pageload transaction with faulty timestamps for slow loading pages', async ({
144160
page,
145161
}) => {

packages/nextjs/src/common/withServerActionInstrumentation.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { logger, tracingContextFromHeaders } from '@sentry/utils';
1111

1212
import { DEBUG_BUILD } from './debug-build';
13+
import { isNotFoundNavigationError, isRedirectNavigationError } from './nextNavigationErrorUtils';
1314
import { platformSupportsStreaming } from './utils/platformSupportsStreaming';
1415
import { flushQueue } from './utils/responseEnd';
1516

@@ -101,7 +102,19 @@ async function withServerActionInstrumentationImplementation<A extends (...args:
101102
},
102103
async span => {
103104
const result = await handleCallbackErrors(callback, error => {
104-
captureException(error, { mechanism: { handled: false } });
105+
if (isNotFoundNavigationError(error)) {
106+
// We don't want to report "not-found"s
107+
span?.setStatus('not_found');
108+
} else if (isRedirectNavigationError(error)) {
109+
// Don't do anything for redirects
110+
} else {
111+
span?.setStatus('internal_error');
112+
captureException(error, {
113+
mechanism: {
114+
handled: false,
115+
},
116+
});
117+
}
105118
});
106119

107120
if (options.recordResponse !== undefined ? options.recordResponse : sendDefaultPii) {

0 commit comments

Comments
 (0)