diff --git a/packages/next/src/client/components/error-boundary.tsx b/packages/next/src/client/components/error-boundary.tsx
index b6444a57d2a3f..13d87fb40c18e 100644
--- a/packages/next/src/client/components/error-boundary.tsx
+++ b/packages/next/src/client/components/error-boundary.tsx
@@ -2,6 +2,7 @@
import React from 'react'
import { usePathname } from './navigation'
+import { isNextRouterError } from './is-next-router-error'
const styles = {
error: {
@@ -73,6 +74,12 @@ export class ErrorBoundaryHandler extends React.Component<
}
static getDerivedStateFromError(error: Error) {
+ if (isNextRouterError(error)) {
+ // Re-throw if an expected internal Next.js router error occurs
+ // this means it should be handled by a different boundary (such as a NotFound boundary in a parent segment)
+ throw error
+ }
+
return { error }
}
diff --git a/test/e2e/app-dir/not-found-default/index.test.ts b/test/e2e/app-dir/not-found-default/index.test.ts
index 06037098b3751..fb882e9e5a22b 100644
--- a/test/e2e/app-dir/not-found-default/index.test.ts
+++ b/test/e2e/app-dir/not-found-default/index.test.ts
@@ -21,13 +21,6 @@ createNextDescribe(
)
return 'success'
}, /success/)
- } else {
- expect(await browser.elementByCss('h2').text()).toBe(
- 'Application error: a server-side exception has occurred (see the server logs for more information).'
- )
- expect(await browser.elementByCss('p').text()).toBe(
- 'Digest: NEXT_NOT_FOUND'
- )
}
})
@@ -54,13 +47,6 @@ createNextDescribe(
expect(await getRedboxDescription(browser)).toBe(
'Error: notFound() is not allowed to use in root layout'
)
- } else {
- expect(await browser.elementByCss('h2').text()).toBe(
- 'Application error: a server-side exception has occurred (see the server logs for more information).'
- )
- expect(await browser.elementByCss('p').text()).toBe(
- 'Digest: NEXT_NOT_FOUND'
- )
}
})
diff --git a/test/e2e/app-dir/not-found/basic/app/error-boundary/error.js b/test/e2e/app-dir/not-found/basic/app/error-boundary/error.js
new file mode 100644
index 0000000000000..35cc32fa29604
--- /dev/null
+++ b/test/e2e/app-dir/not-found/basic/app/error-boundary/error.js
@@ -0,0 +1,4 @@
+'use client'
+export default function Error() {
+ return
There was an error
+}
diff --git a/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/[dynamic]/page.js b/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/[dynamic]/page.js
new file mode 100644
index 0000000000000..623218d0e366b
--- /dev/null
+++ b/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/[dynamic]/page.js
@@ -0,0 +1,10 @@
+import { notFound } from 'next/navigation'
+import React from 'react'
+
+export default function Page({ params }) {
+ if (params.dynamic === 'trigger-not-found') {
+ notFound()
+ }
+
+ return Hello World
+}
diff --git a/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/nested-2/error.js b/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/nested-2/error.js
new file mode 100644
index 0000000000000..35cc32fa29604
--- /dev/null
+++ b/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/nested-2/error.js
@@ -0,0 +1,4 @@
+'use client'
+export default function Error() {
+ return There was an error
+}
diff --git a/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/nested-2/page.js b/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/nested-2/page.js
new file mode 100644
index 0000000000000..926b94c6512b9
--- /dev/null
+++ b/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/nested-2/page.js
@@ -0,0 +1,18 @@
+'use client'
+import { notFound } from 'next/navigation'
+import React from 'react'
+export default function Page() {
+ const [shouldError, setShouldError] = React.useState(false)
+ if (shouldError) {
+ notFound()
+ }
+ return (
+
+ )
+}
diff --git a/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/not-found.js b/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/not-found.js
new file mode 100644
index 0000000000000..8eb7f0bd63b60
--- /dev/null
+++ b/test/e2e/app-dir/not-found/basic/app/error-boundary/nested/not-found.js
@@ -0,0 +1,3 @@
+export default function Page() {
+ return Not Found (error-boundary/nested)
+}
diff --git a/test/e2e/app-dir/not-found/basic/app/error-boundary/page.js b/test/e2e/app-dir/not-found/basic/app/error-boundary/page.js
new file mode 100644
index 0000000000000..926b94c6512b9
--- /dev/null
+++ b/test/e2e/app-dir/not-found/basic/app/error-boundary/page.js
@@ -0,0 +1,18 @@
+'use client'
+import { notFound } from 'next/navigation'
+import React from 'react'
+export default function Page() {
+ const [shouldError, setShouldError] = React.useState(false)
+ if (shouldError) {
+ notFound()
+ }
+ return (
+
+ )
+}
diff --git a/test/e2e/app-dir/not-found/basic/index.test.ts b/test/e2e/app-dir/not-found/basic/index.test.ts
index 3ede15a1cea9c..bbef534c0aa57 100644
--- a/test/e2e/app-dir/not-found/basic/index.test.ts
+++ b/test/e2e/app-dir/not-found/basic/index.test.ts
@@ -8,6 +8,23 @@ createNextDescribe(
skipDeployment: true,
},
({ next, isNextDev, isNextStart }) => {
+ it("should propagate notFound errors past a segment's error boundary", async () => {
+ let browser = await next.browser('/error-boundary')
+ await browser.elementByCss('button').click()
+ expect(await browser.elementByCss('h1').text()).toBe('Root Not Found')
+
+ browser = await next.browser('/error-boundary/nested/nested-2')
+ await browser.elementByCss('button').click()
+ expect(await browser.elementByCss('h1').text()).toBe(
+ 'Not Found (error-boundary/nested)'
+ )
+
+ browser = await next.browser('/error-boundary/nested/trigger-not-found')
+ expect(await browser.elementByCss('h1').text()).toBe(
+ 'Not Found (error-boundary/nested)'
+ )
+ })
+
if (isNextStart) {
it('should include not found client reference manifest in the file trace', async () => {
const fileTrace = JSON.parse(