diff --git a/packages/nuxt/src/core/runtime/nitro/error.ts b/packages/nuxt/src/core/runtime/nitro/error.ts index d0f176b189..d83512f6bc 100644 --- a/packages/nuxt/src/core/runtime/nitro/error.ts +++ b/packages/nuxt/src/core/runtime/nitro/error.ts @@ -45,14 +45,20 @@ export default async function errorhandler (error: H3Error, return send(event, JSON.stringify(errorObject)) } + // Access request headers + const reqHeaders = getRequestHeaders(event) + + // Detect to avoid recursion in SSR rendering of errors + const isRenderingError = event.path.startsWith('/__nuxt_error') || !!reqHeaders['x-nuxt-error'] + // HTML response (via SSR) - const isErrorPage = event.path.startsWith('/__nuxt_error') - const res = !isErrorPage - ? await useNitroApp().localFetch(withQuery(joinURL(useRuntimeConfig().app.baseURL, '/__nuxt_error'), errorObject), { - headers: getRequestHeaders(event) as Record, + const res = isRenderingError ? null : await useNitroApp().localFetch( + withQuery(joinURL(useRuntimeConfig().app.baseURL, '/__nuxt_error'), errorObject), + { + headers: { ...reqHeaders, 'x-nuxt-error': 'true' }, redirect: 'manual' - }).catch(() => null) - : null + } + ).catch(() => null) // Fallback to static rendered error page if (!res) { diff --git a/test/basic.test.ts b/test/basic.test.ts index 31e3d919e7..c99c2face4 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -814,6 +814,17 @@ describe('errors', () => { "url": "/__nuxt_error", } `) + + it('should not recursively throw an error when there is an error rendering the error page', async () => { + const res = await $fetch('/', { + headers: { + 'x-test-recurse-error': 'true', + accept: 'text/html' + } + }) + expect(typeof res).toBe('string') + expect(res).toContain('Hello Nuxt 3!') + }) }) // TODO: need to create test for webpack diff --git a/test/fixtures/basic/plugins/error.server.ts b/test/fixtures/basic/plugins/error.server.ts new file mode 100644 index 0000000000..86d5255786 --- /dev/null +++ b/test/fixtures/basic/plugins/error.server.ts @@ -0,0 +1,5 @@ +export default defineNuxtPlugin(async () => { + if (useRequestHeaders(['x-test-recurse-error'])['x-test-recurse-error']) { + await useRequestFetch()('/api/error').catch(() => {}) + } +}) diff --git a/test/fixtures/basic/server/api/error.ts b/test/fixtures/basic/server/api/error.ts new file mode 100644 index 0000000000..d83567557b --- /dev/null +++ b/test/fixtures/basic/server/api/error.ts @@ -0,0 +1,3 @@ +export default defineEventHandler(async () => { + throw createError({ statusCode: 400 }) +})