diff --git a/.changeset/purple-rats-wink.md b/.changeset/purple-rats-wink.md new file mode 100644 index 000000000000..426cb612d2c1 --- /dev/null +++ b/.changeset/purple-rats-wink.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Render generic error page if \_\_layout returns error while rendering full error page diff --git a/packages/kit/src/runtime/load.js b/packages/kit/src/runtime/load.js index 5eaac5988d36..974ba0ff104a 100644 --- a/packages/kit/src/runtime/load.js +++ b/packages/kit/src/runtime/load.js @@ -26,7 +26,10 @@ export function normalize(loaded) { const status = loaded.status; if (!loaded.error && has_error_status) { - return { status: status || 500, error: new Error() }; + return { + status: status || 500, + error: new Error(`${status}`) + }; } const error = typeof loaded.error === 'string' ? new Error(loaded.error) : loaded.error; diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 9140a00018fb..16b90f07537e 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -3,7 +3,7 @@ import { render_page } from './page/index.js'; import { render_response } from './page/render.js'; import { respond_with_error } from './page/respond_with_error.js'; import { coalesce_to_error } from '../../utils/error.js'; -import { decode_params, serialize_error } from './utils.js'; +import { decode_params, serialize_error, GENERIC_ERROR } from './utils.js'; import { normalize_path } from '../../utils/url.js'; import { exec } from '../../utils/routing.js'; import { negotiate } from '../../utils/http.js'; @@ -274,6 +274,12 @@ export async function respond(request, options, state) { } } + if (state.initiator === GENERIC_ERROR) { + return new Response('Internal Server Error', { + status: 500 + }); + } + // if this request came direct from the user, rather than // via a `fetch` in a `load`, render a 404 page if (!state.initiator) { @@ -328,6 +334,7 @@ export async function respond(request, options, state) { }); } + // TODO is this necessary? should we just return a plain 500 at this point? try { const $session = await options.hooks.getSession(event); return await respond_with_error({ diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index de6df8cd25d6..1cab2f9c71c0 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -13,14 +13,13 @@ import { domain_matches, path_matches } from './cookie.js'; * event: import('types').RequestEvent; * options: import('types').SSROptions; * state: import('types').SSRState; - * route: import('types').SSRPage | null; + * route: import('types').SSRPage | import('types').SSRErrorPage; * node: import('types').SSRNode; * $session: any; * stuff: Record; - * is_error: boolean; * is_leaf: boolean; - * status?: number; - * error?: Error; + * status: number | null; + * error: Error | null; * }} opts * @returns {Promise} */ @@ -32,7 +31,6 @@ export async function load_node({ node, $session, stuff, - is_error, is_leaf, status, error @@ -344,8 +342,8 @@ export async function load_node({ return proxy; }, stuff: { ...stuff }, - status: (is_error ? status : shadow.status) ?? null, - error: is_error ? error ?? null : null + status: shadow.status ?? status ?? null, + error: error ?? null }; if (options.dev) { diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 6238e4270e5a..512162b5f715 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -23,7 +23,7 @@ const updated = { * state: import('types').SSRState; * $session: any; * page_config: { hydrate: boolean, router: boolean }; - * status: number; + * status: number | null; * error: Error | null; * event: import('types').RequestEvent; * resolve_opts: import('types').RequiredResolveOptions; @@ -128,7 +128,7 @@ export async function render_response({ error, params: event.params, routeId: event.routeId, - status, + status: status || 200, stuff, url: state.prerendering ? new PrerenderingURL(event.url) : event.url }, @@ -321,7 +321,7 @@ export async function render_response({ } return new Response(html, { - status, + status: status || 200, headers }); } diff --git a/packages/kit/src/runtime/server/page/respond.js b/packages/kit/src/runtime/server/page/respond.js index 961a6b1680be..ed9ad09df2b8 100644 --- a/packages/kit/src/runtime/server/page/respond.js +++ b/packages/kit/src/runtime/server/page/respond.js @@ -36,7 +36,7 @@ export async function respond(opts) { hydrate: true, router: true }, - status: 200, + status: null, error: null, event, stuff: {} @@ -84,8 +84,8 @@ export async function respond(opts) { /** @type {Array} */ let branch = []; - /** @type {number} */ - let status = 200; + /** @type {number | null} */ + let status = null; /** @type {Error | null} */ let error = null; @@ -105,10 +105,15 @@ export async function respond(opts) { if (node) { try { loaded = await load_node({ - ...opts, + event, + options, + state, + route, node, + $session, stuff, - is_error: false, + status, + error, is_leaf: i === nodes.length - 1 }); @@ -159,10 +164,13 @@ export async function respond(opts) { try { const error_loaded = /** @type {import('./types').Loaded} */ ( await load_node({ - ...opts, + event, + options, + state, + route, + $session, node: error_node, stuff: node_loaded.stuff, - is_error: true, is_leaf: false, status, error diff --git a/packages/kit/src/runtime/server/page/respond_with_error.js b/packages/kit/src/runtime/server/page/respond_with_error.js index cac5b4fc34b8..bc9532e2ce9a 100644 --- a/packages/kit/src/runtime/server/page/respond_with_error.js +++ b/packages/kit/src/runtime/server/page/respond_with_error.js @@ -1,6 +1,7 @@ import { render_response } from './render.js'; import { load_node } from './load_node.js'; import { coalesce_to_error } from '../../../utils/error.js'; +import { GENERIC_ERROR } from '../utils.js'; /** * @typedef {import('./types.js').Loaded} Loaded @@ -41,25 +42,29 @@ export async function respond_with_error({ event, options, state, - route: null, + route: GENERIC_ERROR, node: default_layout, $session, stuff: {}, - is_error: false, + status, + error, is_leaf: false }) ); + if (layout_loaded.loaded.error) { + throw layout_loaded.loaded.error; + } + const error_loaded = /** @type {Loaded} */ ( await load_node({ event, options, state, - route: null, + route: GENERIC_ERROR, node: default_error, $session, stuff: layout_loaded ? layout_loaded.stuff : {}, - is_error: true, is_leaf: false, status, error diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js index de9da9cf660c..6e68070b54b5 100644 --- a/packages/kit/src/runtime/server/utils.js +++ b/packages/kit/src/runtime/server/utils.js @@ -96,3 +96,8 @@ function clone_error(error, get_stack) { return object; } + +/** @type {import('types').SSRErrorPage} */ +export const GENERIC_ERROR = { + id: '__error' +}; diff --git a/packages/kit/test/apps/basics/src/routes/__layout.svelte b/packages/kit/test/apps/basics/src/routes/__layout.svelte index c5421be53848..79f8f93df801 100644 --- a/packages/kit/test/apps/basics/src/routes/__layout.svelte +++ b/packages/kit/test/apps/basics/src/routes/__layout.svelte @@ -1,8 +1,26 @@ +

layout status: {status}

{foo.bar}