feat: allow error boundaries to catch errors on the server#15308
feat: allow error boundaries to catch errors on the server#15308dummdidumm wants to merge 22 commits intomainfrom
Conversation
Take advantage of sveltejs/svelte#17672 to add the `handleError` hook as `transformError` so that error boundaries run on the server. Behind an experimental flag. Closes #14808 Closes #14410 Closes #14398
🦋 Changeset detectedLatest commit: 4546cb3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
| remoteFunctions: false, | ||
| forkPreloads: false | ||
| forkPreloads: false, | ||
| serverErrorBoundaries: false |
There was a problem hiding this comment.
I don't have a great alternative suggestion but I feel like serverErrorBoundaries doesn't totally cover it, since this also affects the behaviour of boundaries client-side
|
|
||
| if (errors && __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__) { | ||
| let last_idx = -1; | ||
| result.props.errors = ( |
There was a problem hiding this comment.
in an ideal world, we wouldn't need to do all this — the error boundary self-activate. that would involve turning load errors into render errors. how achievable does that sound? (if the answer is 'lol not at all' then that's fine)
There was a problem hiding this comment.
...maybe by making the data prop a getter that throws? 🤔
There was a problem hiding this comment.
I don't know what you mean by this. This is about loading the +error.svelte components. We have to do this here upfront because we can't rely on using await in the template - it's still experimental and will be for a while.
|
|
||
| /** @type {Array<import('types').SSRComponent | undefined> | undefined} */ | ||
| let error_components; | ||
| if (options.server_error_boundaries && ssr) { |
There was a problem hiding this comment.
This will get the tests in sveltejs/kit#15308 green, right now they fail because page state cannot be found because context not available
This will get the tests in sveltejs/kit#15308 green, right now they fail because page state cannot be found because context not available
… double colons instead of single colon. This commit fixes the issue reported at documentation/docs/30-advanced/25-errors.md:149 **Bug Explanation:** In the SvelteKit documentation file `documentation/docs/30-advanced/25-errors.md`, there is a typo in a code example demonstrating the use of `<svelte:boundary>` for error handling. The closing tag was written as `</svelte::boundary>` (with two colons) instead of `</svelte:boundary>` (with one colon). This is inconsistent with the opening tag `<svelte:boundary>` which correctly uses a single colon. This typo would confuse developers reading the documentation, as it shows invalid Svelte syntax. Users copying this code example would get a syntax error when trying to use it. **Fix Explanation:** Changed the closing tag from `</svelte::boundary>` to `</svelte:boundary>` to match the opening tag and use the correct Svelte special element syntax. Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: dummdidumm <sholthausen@web.de>
…after `error` has been reassigned to the transformed `App.Error`, causing the original HTTP status to always be replaced with 500. This commit fixes the issue reported at packages/kit/src/runtime/server/page/render.js:194 ## Bug Explanation The bug is located in `packages/kit/src/runtime/server/page/render.js` in the `transformError` callback (line 194). ### Why it happens: 1. The `transformError` callback receives `e` as a parameter - this is the original error which could be an `HttpError` (e.g., from `error(403, 'Forbidden')`) or a `SvelteKitError`, both of which have a `.status` property. 2. The callback calls `handle_error_and_jsonify(event, event_state, options, e)` which transforms the error into an `App.Error` object. This `App.Error` is NOT an `HttpError` or `SvelteKitError`. 3. The code then reassigns: `props.page.error = props.error = error = transformed;` 4. The bug: `props.page.status = status = get_status(error);` - This calls `get_status()` on the transformed error (now an `App.Error`), NOT the original error. 5. The `get_status()` function (in `packages/kit/src/utils/error.js`) checks: `error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500` 6. Since the transformed `App.Error` is neither `HttpError` nor `SvelteKitError`, `get_status()` always returns 500. ### Impact: When a component throws `error(403, 'Forbidden')` or any other HTTP error during rendering (when `handleRenderingErrors` is enabled), the 403 status would be lost and replaced with 500. This affects the HTTP response status code and the page's status property. ## Fix Explanation Changed `get_status(error)` to `get_status(e)` on line 194. This ensures we extract the status from the original error `e` (which may be an `HttpError` or `SvelteKitError` with the correct status) rather than the transformed error which is always `App.Error`. The fix is minimal and targeted - it only changes the argument passed to `get_status()` to use the correct variable that still contains the original error with its status information. Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: dummdidumm <sholthausen@web.de>
…with 500 when errors are transformed in the client-side `transformError` callback because `get_status()` is called on the transformed `App.Error` instead of the original error.
This commit fixes the issue reported at packages/kit/src/runtime/client/client.js:622
## Bug Explanation
The bug exists in the client-side `transformError` callback when the experimental error handling feature is enabled.
### How it happens:
1. When an error is thrown (e.g., `error(403, 'Forbidden')`), it's passed to `transformError` as parameter `e`
2. The error is transformed via `handle_error(e, ...)` which returns an `App.Error` object (a user-friendly error representation)
3. `get_status(error)` is then called on the **transformed** error to set the HTTP status
4. However, `get_status()` (in `packages/kit/src/utils/error.js`) only extracts the correct status from `HttpError` or `SvelteKitError` instances - for any other type, it returns 500
5. Since the transformed error is an `App.Error` (plain object), `get_status()` always returns 500, losing the original status
### Impact:
If a user throws `error(403, 'Forbidden')` in a component, the page would incorrectly show status 500 instead of 403. This breaks the expected behavior where `$page.status` should reflect the original error status.
## Fix Explanation
The fix changes `get_status(error)` to `get_status(e)` in the client-side code (`packages/kit/src/runtime/client/client.js` line 621):
* Before: `rendering_error = { error, status: get_status(error) };`
* After: `rendering_error = { error, status: get_status(e) };`
By calling `get_status(e)` on the **original** error (parameter `e`) before transformation, we correctly extract the HTTP status from the `HttpError` or `SvelteKitError` instance, preserving the intended status code (403, 404, etc.).
Note: The server-side fix in `render.js` was already applied in a separate commit.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: dummdidumm <sholthausen@web.de>
Take advantage of sveltejs/svelte#17672 to add the
handleErrorhook astransformErrorso that error boundaries run on the server. Behind an experimental flag.Closes #14808
Closes #14410
Closes #14398
Closes #14932
New tests fail right now, install the other PR via pkg.new locally to see it in effect.
Todos:
page.status, should it be passed as a prop to+error.svelte, too? What would it be in case of a rendering error though?handleErrorreceives one, but for render errors we don't have this context. Do a best effort of a stub, like "this was the URL this happened on" etc? Or just passnull? Solved: We can use the last navigation event we know ofPlease don't delete this checklist! Before submitting the PR, please make sure you do the following:
Tests
pnpm testand lint the project withpnpm lintandpnpm checkChangesets
pnpm changesetand following the prompts. Changesets that add features should beminorand those that fix bugs should bepatch. Please prefix changeset messages withfeat:,fix:, orchore:.