Skip to content

Commit

Permalink
fix(nextjs): Don't report React postpone errors (#12194)
Browse files Browse the repository at this point in the history
  • Loading branch information
lforst authored May 24, 2024
1 parent a9cef35 commit 41951ba
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -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 <div>This server component will throw a PPR error that we do not want to catch.</div>;
}
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
});
27 changes: 26 additions & 1 deletion packages/nextjs/src/server/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -181,6 +181,31 @@ export function init(options: NodeOptions): void {
),
);

getGlobalScope().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);
}
Expand Down

0 comments on commit 41951ba

Please sign in to comment.