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);
+});
diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts
index acfa4f03af97..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,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);
}