diff --git a/packages/next/src/server/route-modules/pages/pages-handler.ts b/packages/next/src/server/route-modules/pages/pages-handler.ts
index 22e527c7c7db73..68bfabccfc16f8 100644
--- a/packages/next/src/server/route-modules/pages/pages-handler.ts
+++ b/packages/next/src/server/route-modules/pages/pages-handler.ts
@@ -693,11 +693,10 @@ export const getHandler = ({
}
// when invoking _error before pages/500 we don't actually
- // send the _error response
- if (
- getRequestMeta(req, 'customErrorRender') ||
- (isErrorPage && isMinimalMode && res.statusCode === 500)
- ) {
+ // send the _error response - only skip if customErrorRender is set
+ // (meaning pages/500 exists and will be rendered next), or if using
+ // the builtin _error in minimalMode to allow error bubbling
+ if (getRequestMeta(req, 'customErrorRender')) {
return null
}
diff --git a/test/production/pages-get-initial-props-error/next.config.js b/test/production/pages-get-initial-props-error/next.config.js
new file mode 100644
index 00000000000000..807126e4cf0bf5
--- /dev/null
+++ b/test/production/pages-get-initial-props-error/next.config.js
@@ -0,0 +1,6 @@
+/**
+ * @type {import('next').NextConfig}
+ */
+const nextConfig = {}
+
+module.exports = nextConfig
diff --git a/test/production/pages-get-initial-props-error/pages-get-initial-props-error.test.ts b/test/production/pages-get-initial-props-error/pages-get-initial-props-error.test.ts
new file mode 100644
index 00000000000000..1e6774c3ab58ea
--- /dev/null
+++ b/test/production/pages-get-initial-props-error/pages-get-initial-props-error.test.ts
@@ -0,0 +1,17 @@
+import { nextTestSetup } from 'e2e-utils'
+
+describe('pages-get-initial-props-error', () => {
+ const { next } = nextTestSetup({
+ files: __dirname,
+ })
+
+ it('should render _error with 500 status code when getInitialProps throws', async () => {
+ const browser = await next.browser('/gip-error')
+ expect(await browser.elementByCss('p').text()).toBe(
+ 'An error 500 occurred on server'
+ )
+
+ const response = await next.fetch('/gip-error')
+ expect(response.status).toBe(500)
+ })
+})
diff --git a/test/production/pages-get-initial-props-error/pages/_app.tsx b/test/production/pages-get-initial-props-error/pages/_app.tsx
new file mode 100644
index 00000000000000..93928c3da70e23
--- /dev/null
+++ b/test/production/pages-get-initial-props-error/pages/_app.tsx
@@ -0,0 +1,5 @@
+import type { AppProps } from 'next/app'
+
+export default function App({ Component, pageProps }: AppProps) {
+ return
+}
diff --git a/test/production/pages-get-initial-props-error/pages/_document.tsx b/test/production/pages-get-initial-props-error/pages/_document.tsx
new file mode 100644
index 00000000000000..bff2b1b2821cb2
--- /dev/null
+++ b/test/production/pages-get-initial-props-error/pages/_document.tsx
@@ -0,0 +1,13 @@
+import { Html, Head, Main, NextScript } from 'next/document'
+
+export default function Document() {
+ return (
+
+
+
+
+
+
+ )
+}
diff --git a/test/production/pages-get-initial-props-error/pages/_error.tsx b/test/production/pages-get-initial-props-error/pages/_error.tsx
new file mode 100644
index 00000000000000..e281c1e1a3ce21
--- /dev/null
+++ b/test/production/pages-get-initial-props-error/pages/_error.tsx
@@ -0,0 +1,38 @@
+import type { NextPageContext } from 'next'
+
+type ErrorProps = {
+ statusCode?: number
+}
+
+function ErrorPage({ statusCode }: ErrorProps) {
+ return (
+
+ {statusCode !== undefined
+ ? `An error ${statusCode} occurred on server`
+ : 'An error occurred on client'}
+
+ )
+}
+
+ErrorPage.getInitialProps = ({ res, err }: NextPageContext) => {
+ console.log('ErrorPage.gip:err', err, '!!res', !!res)
+ let statusCode = 200
+ try {
+ statusCode = res?.statusCode ?? err?.statusCode ?? (res || err ? 500 : 404)
+ } catch (error) {
+ console.error('ErrorPage.gip:try-catch', error)
+ }
+
+ console.trace(
+ 'ErrorPage.status code',
+ 'res?.statusCode',
+ res?.statusCode,
+ 'err?.statusCode',
+ err?.statusCode,
+ 'final',
+ statusCode
+ )
+ return { statusCode }
+}
+
+export default ErrorPage
diff --git a/test/production/pages-get-initial-props-error/pages/gip-error.tsx b/test/production/pages-get-initial-props-error/pages/gip-error.tsx
new file mode 100644
index 00000000000000..24137289269bc4
--- /dev/null
+++ b/test/production/pages-get-initial-props-error/pages/gip-error.tsx
@@ -0,0 +1,21 @@
+import type { GetServerSideProps } from 'next'
+
+class IntentionalServerError extends Error {
+ statusCode: number
+
+ constructor(message: string, statusCode = 500) {
+ super(message)
+ this.name = 'IntentionalServerError'
+ this.statusCode = statusCode
+ }
+}
+
+export default function ErrorDemo() {
+ return Custom Error Page Demo
+}
+
+export const getServerSideProps: GetServerSideProps = async () => {
+ throw new IntentionalServerError(
+ 'Intentional error triggered via getServerSideProps.'
+ )
+}
diff --git a/test/production/required-server-files-ssr-404/test/index.test.ts b/test/production/required-server-files-ssr-404/test/index.test.ts
index 56be852c7420c4..75290c1e4e1d6f 100644
--- a/test/production/required-server-files-ssr-404/test/index.test.ts
+++ b/test/production/required-server-files-ssr-404/test/index.test.ts
@@ -551,19 +551,31 @@ describe('Required Server Files', () => {
it('should bubble error correctly for gip page', async () => {
const res = await fetchViaHTTP(appPort, '/errors/gip', { crash: '1' })
expect(res.status).toBe(500)
- expect(await res.text()).toBe('Internal Server Error')
+ const body = await res.text()
+ const $ = cheerio.load(body)
+
+ expect($('h1').text()).toBe('500')
+ expect($('h2').text()).toBe('Internal Server Error.')
})
it('should bubble error correctly for gssp page', async () => {
const res = await fetchViaHTTP(appPort, '/errors/gssp', { crash: '1' })
expect(res.status).toBe(500)
- expect(await res.text()).toBe('Internal Server Error')
+ const body = await res.text()
+ const $ = cheerio.load(body)
+
+ expect($('h1').text()).toBe('500')
+ expect($('h2').text()).toBe('Internal Server Error.')
})
it('should bubble error correctly for gsp page', async () => {
const res = await fetchViaHTTP(appPort, '/errors/gsp/crash')
expect(res.status).toBe(500)
- expect(await res.text()).toBe('Internal Server Error')
+ const body = await res.text()
+ const $ = cheerio.load(body)
+
+ expect($('h1').text()).toBe('500')
+ expect($('h2').text()).toBe('Internal Server Error.')
})
it('should normalize optional values correctly for SSP page', async () => {
diff --git a/test/production/standalone-mode/required-server-files/required-server-files.test.ts b/test/production/standalone-mode/required-server-files/required-server-files.test.ts
index 3a2ce8fffdd3dd..246619f210dd0e 100644
--- a/test/production/standalone-mode/required-server-files/required-server-files.test.ts
+++ b/test/production/standalone-mode/required-server-files/required-server-files.test.ts
@@ -1060,7 +1060,10 @@ describe('required server files', () => {
it('should bubble error correctly for gip page', async () => {
const res = await fetchViaHTTP(appPort, '/errors/gip', { crash: '1' })
expect(res.status).toBe(500)
- expect(await res.text()).toBe('Internal Server Error')
+ const body = await res.text()
+ const $ = cheerio.load(body)
+ expect($('h1').text()).toBe('500')
+ expect($('h2').text()).toBe('Internal Server Error.')
await retry(() => {
expect(errors.join('\n')).toInclude('gip hit an oops')
@@ -1070,7 +1073,10 @@ describe('required server files', () => {
it('should bubble error correctly for gssp page', async () => {
const res = await fetchViaHTTP(appPort, '/errors/gssp', { crash: '1' })
expect(res.status).toBe(500)
- expect(await res.text()).toBe('Internal Server Error')
+ const body = await res.text()
+ const $ = cheerio.load(body)
+ expect($('h1').text()).toBe('500')
+ expect($('h2').text()).toBe('Internal Server Error.')
await retry(() => {
expect(errors.join('\n')).toInclude('gssp hit an oops')
@@ -1080,7 +1086,10 @@ describe('required server files', () => {
it('should bubble error correctly for gsp page', async () => {
const res = await fetchViaHTTP(appPort, '/errors/gsp/crash')
expect(res.status).toBe(500)
- expect(await res.text()).toBe('Internal Server Error')
+ const body = await res.text()
+ const $ = cheerio.load(body)
+ expect($('h1').text()).toBe('500')
+ expect($('h2').text()).toBe('Internal Server Error.')
await retry(() => {
expect(errors.join('\n')).toInclude('gsp hit an oops')