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')
+ })
+})