diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-action/page.tsx b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-action/page.tsx index 18ffe80fe707..4cbeafba4f67 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-action/page.tsx +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-action/page.tsx @@ -1,6 +1,6 @@ import * as Sentry from '@sentry/nextjs'; import { headers } from 'next/headers'; -import { notFound } from 'next/navigation'; +import { notFound, redirect } from 'next/navigation'; export default function ServerComponent() { async function myServerAction(formData: FormData) { @@ -26,6 +26,17 @@ export default function ServerComponent() { ); } + async function redirectServerAction(formData: FormData) { + 'use server'; + return await Sentry.withServerActionInstrumentation( + 'redirectServerAction', + { formData, headers: headers(), recordResponse: true }, + () => { + redirect('/'); + }, + ); + } + return ( <> {/* @ts-ignore */} @@ -38,6 +49,11 @@ export default function ServerComponent() { + {/* @ts-ignore */} +
+ + +
); } diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts index 5656ce0e5e57..3ad9e43f06db 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts @@ -1,5 +1,5 @@ import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; +import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; const packageJson = require('../package.json'); @@ -108,19 +108,49 @@ test('Should set not_found status for server actions calling notFound()', async const nextjsMajor = Number(nextjsVersion.split('.')[0]); test.skip(!isNaN(nextjsMajor) && nextjsMajor < 14, 'only applies to nextjs apps >= version 14'); - const serverComponentTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => { + const serverActionTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => { return transactionEvent?.transaction === 'serverAction/notFoundServerAction'; }); await page.goto('/server-action'); await page.getByText('Run NotFound Action').click(); - const transactionEvent = await serverComponentTransactionPromise; + const transactionEvent = await serverActionTransactionPromise; expect(transactionEvent).toBeDefined(); expect(transactionEvent.contexts?.trace?.status).toBe('not_found'); }); +test('Should not capture "NEXT_REDIRECT" control-flow errors for server actions calling redirect()', async ({ + page, +}) => { + const nextjsVersion = packageJson.dependencies.next; + const nextjsMajor = Number(nextjsVersion.split('.')[0]); + test.skip(!isNaN(nextjsMajor) && nextjsMajor < 14, 'only applies to nextjs apps >= version 14'); + + const serverActionTransactionPromise = waitForTransaction('nextjs-app-dir', transactionEvent => { + return transactionEvent?.transaction === 'serverAction/redirectServerAction'; + }); + + let controlFlowErrorCaptured = false; + waitForError('nextjs-app-dir', errorEvent => { + if (errorEvent.exception?.values?.[0].value === 'NEXT_REDIRECT') { + controlFlowErrorCaptured = true; + } + + return false; + }); + + await page.goto('/server-action'); + await page.getByText('Run Redirect Action').click(); + + const serverActionTransactionEvent = await serverActionTransactionPromise; + expect(serverActionTransactionEvent).toBeDefined(); + + // By the time the server action span finishes the error should already have been sent + expect(controlFlowErrorCaptured).toBe(false); +}); + test('Will not include spans in pageload transaction with faulty timestamps for slow loading pages', async ({ page, }) => { diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index c8e6d21837fd..9b1d58610e0d 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -51,7 +51,9 @@ export function init(options: BrowserOptions): Client | undefined { addEventProcessor(filterIncompleteNavigationTransactions); const filterNextRedirectError: EventProcessor = (event, hint) => - isRedirectNavigationError(hint?.originalException) ? null : event; + isRedirectNavigationError(hint?.originalException) || event.exception?.values?.[0]?.value === 'NEXT_REDIRECT' + ? null + : event; filterNextRedirectError.id = 'NextRedirectErrorFilter'; addEventProcessor(filterNextRedirectError);