diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 4bd3cf103e025..69b95d933aa84 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -1628,7 +1628,11 @@ export async function renderToHTMLOrFlight( ) const use404Error = res.statusCode === 404 - const useDefaultError = res.statusCode < 400 || res.statusCode === 307 + // When it's in error state but status code is not 200, we should render global-error + const useGlobalError = + res.statusCode === 200 && process.env.NODE_ENV === 'production' + const useDefaultError = + (res.statusCode < 400 || res.statusCode === 307) && !useGlobalError const { layout } = loaderTree[2] const injectedCSS = new Set() diff --git a/test/e2e/app-dir/error-boundary-and-not-found-linking/app/error.tsx b/test/e2e/app-dir/error-boundary-and-not-found-linking/app/error.tsx index 2d203c0ac82cd..2c07aa5dc7303 100644 --- a/test/e2e/app-dir/error-boundary-and-not-found-linking/app/error.tsx +++ b/test/e2e/app-dir/error-boundary-and-not-found-linking/app/error.tsx @@ -4,10 +4,12 @@ import Link from 'next/link' export default function ErrorComponent() { return ( <> -

Error Happened!

- - To Result - +
+

Error Happened!

+ + To Result + +
) } diff --git a/test/e2e/app-dir/error-boundary-and-not-found-linking/app/layout.tsx b/test/e2e/app-dir/error-boundary-and-not-found-linking/app/layout.tsx index e7077399c03ce..69e2ad5092a0e 100644 --- a/test/e2e/app-dir/error-boundary-and-not-found-linking/app/layout.tsx +++ b/test/e2e/app-dir/error-boundary-and-not-found-linking/app/layout.tsx @@ -1,3 +1,5 @@ +export const revalidate = 0 + export default function Root({ children }: { children: React.ReactNode }) { return ( diff --git a/test/e2e/app-dir/global-error/layout-error.test.ts b/test/e2e/app-dir/global-error/layout-error.test.ts new file mode 100644 index 0000000000000..db83aa336ae1b --- /dev/null +++ b/test/e2e/app-dir/global-error/layout-error.test.ts @@ -0,0 +1,32 @@ +import { getRedboxHeader, hasRedbox } from 'next-test-utils' +import { createNextDescribe } from 'e2e-utils' + +async function testDev(browser, errorRegex) { + expect(await hasRedbox(browser, true)).toBe(true) + expect(await getRedboxHeader(browser)).toMatch(errorRegex) +} + +createNextDescribe( + 'app dir - global error - layout error', + { + files: { + 'app/layout.js': `throw new Error('Global error: layout error')`, + 'app/page.js': `export default function page() { return
Page
}`, + }, + }, + ({ next, isNextDev }) => { + it('should render global error for error in server components', async () => { + const browser = await next.browser('/') + + if (isNextDev) { + await testDev(browser, /Global error: layout error/) + } else { + expect(await browser.elementByCss('h1').text()).toBe('Global Error') + expect(await browser.elementByCss('#error').text()).toBe( + 'Global error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.' + ) + expect(await browser.elementByCss('#digest').text()).toMatch(/\w+/) + } + }) + } +)