From cfc48cb359f0fbd7cfb116137100042b1274b4e2 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 23 May 2024 15:45:45 +0000 Subject: [PATCH 1/4] fix(nextjs): Don't report React postpone errors From e0a1df2c31e76ec7054205d1f47af1110c94afc2 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 23 May 2024 15:46:08 +0000 Subject: [PATCH 2/4] Add failing test --- .../nextjs-15/app/ppr-error/[param]/page.tsx | 18 +++++++++++++++ .../nextjs-15/next.config.js | 6 ++++- .../nextjs-15/tests/ppr-error.test.ts | 22 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15/app/ppr-error/[param]/page.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-15/tests/ppr-error.test.ts diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/app/ppr-error/[param]/page.tsx b/dev-packages/e2e-tests/test-applications/nextjs-15/app/ppr-error/[param]/page.tsx new file mode 100644 index 000000000000..ec2b2b1232c7 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-15/app/ppr-error/[param]/page.tsx @@ -0,0 +1,18 @@ +import * as Sentry from '@sentry/nextjs'; + +export default async function Page({ + searchParams, +}: { + searchParams: { id?: string }; +}) { + try { + console.log(searchParams.id); // Accessing a field on searchParams will throw the PPR error + } catch (e) { + Sentry.captureException(e); // This error should not be reported + await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for any async event processors to run + await Sentry.flush(); + throw e; + } + + return
This server component will throw a PPR error that we do not want to catch.
; +} diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/next.config.js b/dev-packages/e2e-tests/test-applications/nextjs-15/next.config.js index 1098c2ce5a4f..2be749fde774 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15/next.config.js +++ b/dev-packages/e2e-tests/test-applications/nextjs-15/next.config.js @@ -1,7 +1,11 @@ const { withSentryConfig } = require('@sentry/nextjs'); /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + experimental: { + ppr: true, + }, +}; module.exports = withSentryConfig(nextConfig, { silent: true, diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/tests/ppr-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/ppr-error.test.ts new file mode 100644 index 000000000000..1e266fa02541 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/ppr-error.test.ts @@ -0,0 +1,22 @@ +import { expect, test } from '@playwright/test'; +import { waitForError, waitForTransaction } from '@sentry-internal/event-proxy-server'; + +test('should not capture React-internal errors for PPR rendering', async ({ page }) => { + const pageServerComponentTransactionPromise = waitForTransaction('nextjs-15', async transactionEvent => { + return transactionEvent?.transaction === 'Page Server Component (/ppr-error/[param])'; + }); + + let errorEventReceived = false; + waitForError('nextjs-15', async transactionEvent => { + return transactionEvent?.transaction === 'Page Server Component (/ppr-error/[param])'; + }).then(() => { + errorEventReceived = true; + }); + + await page.goto(`/ppr-error/foobar?id=1`); + + const pageServerComponentTransaction = await pageServerComponentTransactionPromise; + expect(pageServerComponentTransaction).toBeDefined(); + + expect(errorEventReceived).toBe(false); +}); From 5665308c4eba780fc0d61f7381bde843234447b2 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 24 May 2024 07:53:04 +0000 Subject: [PATCH 3/4] Add event processor to drop react postpone errors --- packages/nextjs/src/server/index.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index acfa4f03af97..730af67f3fc4 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -181,6 +181,31 @@ export function init(options: NodeOptions): void { ), ); + addEventProcessor( + Object.assign( + ((event, hint) => { + if (event.type !== undefined) { + return event; + } + + const originalException = hint.originalException; + + const isReactControlFlowError = + typeof originalException === 'object' && + originalException !== null && + '$$typeof' in originalException && + originalException.$$typeof === Symbol.for('react.postpone'); + + if (isReactControlFlowError) { + return null; + } + + return event; + }) satisfies EventProcessor, + { id: 'DropReactControlFlowErrors' }, + ), + ); + if (process.env.NODE_ENV === 'development') { addEventProcessor(devErrorSymbolicationEventProcessor); } From 965f8e04c2320e1d7f5efcc0caf7f11943396910 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 24 May 2024 09:20:16 +0000 Subject: [PATCH 4/4] fix --- packages/nextjs/src/server/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index 730af67f3fc4..00cc694f079a 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -1,4 +1,4 @@ -import { addEventProcessor, applySdkMetadata, getClient } from '@sentry/core'; +import { addEventProcessor, applySdkMetadata, getClient, getGlobalScope } from '@sentry/core'; import { getDefaultIntegrations, init as nodeInit } from '@sentry/node'; import type { NodeOptions } from '@sentry/node'; import { GLOBAL_OBJ, logger } from '@sentry/utils'; @@ -181,7 +181,7 @@ export function init(options: NodeOptions): void { ), ); - addEventProcessor( + getGlobalScope().addEventProcessor( Object.assign( ((event, hint) => { if (event.type !== undefined) {