diff --git a/packages/next/src/server/app-render/create-error-handler.tsx b/packages/next/src/server/app-render/create-error-handler.tsx index b67fb304b4..008e152990 100644 --- a/packages/next/src/server/app-render/create-error-handler.tsx +++ b/packages/next/src/server/app-render/create-error-handler.tsx @@ -107,7 +107,13 @@ export function createErrorHandler({ }) } - if (!silenceLogger) { + if ( + (!silenceLogger && + // Only log the error from SSR rendering errors and flight data render errors, + // as RSC renderer error will still be pipped into SSR renderer as well. + source === 'html') || + source === 'flightData' + ) { if (errorLogger) { errorLogger(err).catch(() => {}) } else { diff --git a/test/e2e/app-dir/dedupe-rsc-error-log/app/client/edge/page.tsx b/test/e2e/app-dir/dedupe-rsc-error-log/app/client/edge/page.tsx new file mode 100644 index 0000000000..9f945bef43 --- /dev/null +++ b/test/e2e/app-dir/dedupe-rsc-error-log/app/client/edge/page.tsx @@ -0,0 +1,7 @@ +'use client' + +import { ErrorComponent } from '../../component' + +export default () => + +export const runtime = 'edge' diff --git a/test/e2e/app-dir/dedupe-rsc-error-log/app/client/page.tsx b/test/e2e/app-dir/dedupe-rsc-error-log/app/client/page.tsx new file mode 100644 index 0000000000..a25f860e19 --- /dev/null +++ b/test/e2e/app-dir/dedupe-rsc-error-log/app/client/page.tsx @@ -0,0 +1,7 @@ +'use client' + +import { ErrorComponent } from '../component' + +export default () => + +export const dynamic = 'force-dynamic' diff --git a/test/e2e/app-dir/dedupe-rsc-error-log/app/component.tsx b/test/e2e/app-dir/dedupe-rsc-error-log/app/component.tsx new file mode 100644 index 0000000000..a5e6d3411a --- /dev/null +++ b/test/e2e/app-dir/dedupe-rsc-error-log/app/component.tsx @@ -0,0 +1,8 @@ +async function getData(name: string) { + throw new Error('Custom error:' + name) +} + +export async function ErrorComponent({ name }: { name: string }) { + await getData(name) + return null +} diff --git a/test/e2e/app-dir/dedupe-rsc-error-log/app/layout.tsx b/test/e2e/app-dir/dedupe-rsc-error-log/app/layout.tsx new file mode 100644 index 0000000000..6a37cb1c70 --- /dev/null +++ b/test/e2e/app-dir/dedupe-rsc-error-log/app/layout.tsx @@ -0,0 +1,11 @@ +import { ReactNode } from 'react' + +export default function Root({ children }: { children: ReactNode }) { + return ( + + {children} + + ) +} + +export const dynamic = 'force-dynamic' diff --git a/test/e2e/app-dir/dedupe-rsc-error-log/app/server/edge/page.tsx b/test/e2e/app-dir/dedupe-rsc-error-log/app/server/edge/page.tsx new file mode 100644 index 0000000000..956cdaa660 --- /dev/null +++ b/test/e2e/app-dir/dedupe-rsc-error-log/app/server/edge/page.tsx @@ -0,0 +1,5 @@ +import { ErrorComponent } from '../../component' + +export default () => + +export const runtime = 'edge' diff --git a/test/e2e/app-dir/dedupe-rsc-error-log/app/server/page.tsx b/test/e2e/app-dir/dedupe-rsc-error-log/app/server/page.tsx new file mode 100644 index 0000000000..4555cd08c6 --- /dev/null +++ b/test/e2e/app-dir/dedupe-rsc-error-log/app/server/page.tsx @@ -0,0 +1,5 @@ +import { ErrorComponent } from '../component' + +export default () => + +export const dynamic = 'force-dynamic' diff --git a/test/e2e/app-dir/dedupe-rsc-error-log/dedupe-rsc-error-log.test.ts b/test/e2e/app-dir/dedupe-rsc-error-log/dedupe-rsc-error-log.test.ts new file mode 100644 index 0000000000..026aa8e48a --- /dev/null +++ b/test/e2e/app-dir/dedupe-rsc-error-log/dedupe-rsc-error-log.test.ts @@ -0,0 +1,36 @@ +import { nextTestSetup } from 'e2e-utils' +import { retry } from 'next-test-utils' + +async function expectContainOnce(next: any, search: string) { + // Ensure the search string is found once + await retry(() => { + const parts = next.cliOutput.split(search) + expect(parts.length).toBe(2) + }) +} + +describe('dedupe-rsc-error-log', () => { + const { next } = nextTestSetup({ + files: __dirname, + }) + + it('should only log RSC error once for nodejs runtime', async () => { + await next.fetch('/server') + await expectContainOnce(next, 'Custom error:server-node') + }) + + it('should only log RSC error once for edge runtime', async () => { + await next.fetch('/server/edge') + await expectContainOnce(next, 'Custom error:server-edge') + }) + + it('should only log SSR error once for nodejs runtime', async () => { + await next.fetch('/client') + await expectContainOnce(next, 'Custom error:client-node') + }) + + it('should only log SSR error once for edge runtime', async () => { + await next.fetch('/client/edge') + await expectContainOnce(next, 'Custom error:client-edge') + }) +})