kit: Rejecting streamed data
Describe the bug
When rejecting promises for streamed data, dev server usually crashes, and when it doesn’t crash it doesn’t show my custom error message.
Reproduction
https://stackblitz.com/edit/sveltejs-kit-template-default-2dz1kb?file=src/routes/+page.server.js
Logs
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "Not Found".] {
code: 'ERR_UNHANDLED_REJECTION'
}
System Info
System:
OS: Linux 5.19 Ubuntu 22.04.2 LTS 22.04.2 LTS (Jammy Jellyfish)
CPU: (12) x64 AMD Ryzen 5 5600X 6-Core Processor
Memory: 7.30 GB / 15.53 GB
Container: Yes
Shell: 5.8.1 - /usr/bin/zsh
Binaries:
Node: 18.15.0 - ~/.nvm/versions/node/v18.15.0/bin/node
npm: 9.5.0 - ~/.nvm/versions/node/v18.15.0/bin/npm
Browsers:
Firefox: 112.0.1
npmPackages:
@sveltejs/adapter-node: ^1.2.2 => 1.2.3
@sveltejs/kit: ^1.15.7 => 1.15.7
svelte: ^3.58.0 => 3.58.0
vite: ^4.3.1 => 4.3.1
Severity
serious, but I can work around it
Additional Information
No response
About this issue
- Original URL
- State: open
- Created a year ago
- Reactions: 14
- Comments: 21 (7 by maintainers)
Commits related to this issue
- Handle waypoints without a market Streaming data is not possible when the promise may reject. See: https://github.com/sveltejs/kit/issues/9785 — committed to MLNW/skeleton-traders by MLNW a year ago
- fix: make sure promises from fetch handle errors Ensures that people using `fetch` directly in their load functions don't run into uncaught promise errors with streaming. Also adds some docs for this... — committed to sveltejs/kit by dummdidumm 7 months ago
- fix: make sure promises from fetch handle errors Ensures that people using `fetch` directly in their load functions don't run into uncaught promise errors with streaming. Also adds some docs for this... — committed to sveltejs/kit by dummdidumm 7 months ago
- fix: make sure promises from fetch handle errors (#11228) Ensures that people using `fetch` directly in their load functions don't run into uncaught promise errors with streaming. Also adds some doc... — committed to sveltejs/kit by dummdidumm 7 months ago
- fix: log whole error object in default handleError (#9791) closes #9785 People may have custom errors without a stack and logging just the stack as a string misses out on some pretty terminal form... — committed to tanhauhau/kit by gtm-nayan a year ago
This issue makes streamed data mostly unusable. Especially when dealing with fetches that may return a 404 or those that want to throw a redirect. It, for example, prevents us from using redirects to protect authentication sensitive routes.
And whats even worse is that the documentation of streamed data is suggesting that error handling isn’t an issue. The example promise won’t fail
and the error handling
suggests that rejected, streamed data does not crash the server.
Debugging this issue has cost us a lot of time that could have been prevent by mentioning this caveat in the documentation, or even better, implementing a fix. This should be a high priority issue, if you ask me.
@gtm-nayan - I think if this can’t be done for now, you guys need to update the docs explaining this clearly perhaps with an example workaround. If the
await catch
doesn’t work in the template at all, this shouldn’t be the example listed.J
We’ll probably not be able to fix this without changing a few things in a major version because your promises can reject before they even reach SvelteKit.
In the meantime you can use workarounds https://jakearchibald.com/2023/unhandled-rejections/ to prevent crashing your server. Or set up a global promise rejection handler, which would be a good idea regardless of streamed promises.
For the ones that want to throw redirects from their streamed promises, that will not work, ever, because the browser has already received an
OK
response with the rest of the page by that point. The best you can do there is make another navigation on the client side like @eliasdefaria said.Thank you for the workaround, but I am here from my first Svelte (and JS) project, and that seems a bit complex for me.
A friend helped and I got this working. Hope it helps some newer people like me who want to use streaming promises without having the whole app crash and need a more basic workaround.
in
+page.server.ts
Then in my
+page.svelte
Adding this snippet to the bottom of
svelte.config.js
will prevent crashes and help with debugging info during developmentthis is what I am using to resolve this issue
function streamedError(err: {status: number, body: {message: string}}) { return new Promise((fulfil, reject) => { setTimeout(() => { reject(error(err.status, err.body.message)); }, 500); }); }
and then I attach it to the streamed promises as follows:
streamedFunc().catch(err=>streamedError(err))
@adrifer Before you tread down that path, I should let you know that you can’t throw redirects from a streamed promise. The server needs to know the status code before it starts sending the response and it’s too late by the time it gets to your streamed promises.
As for the issue in general, Node.js requires that an error handler must be attached before the tick in which the promise is rejected. In SvelteKit, that’s done by the serializer, but by the time we’re done awaiting all the load functions it may have been too late to attach the handler.
I can’t think of a clean way to solve this yet, I don’t think it’s actually possible with the current API, we’d have to walk the returned objects (arrays, maps, whatever included) as soon as we get it from the user but turns out even that’s not enough.
Consider
the rejected promise is created before the yield point of the timeout so the exception is triggered and the process exits before the promise even reaches SvelteKit.
Perhaps there is some way to kludge this, ensuring we attach the error handlers before any other yield point is reached. ~The handlers would catch the error and put it back on the promise under some special key and the serializer checks the key to get errors that happened before the promise reached the serializer~ (apparently just need to chain a catch handler, and not use the chained promise) but I won’t be too hopeful that it’s viable. We might need something like
defer()
after all.Cannot reproduce the crash with the provided repro on either stackblitz or locally, but can confirm the error reporting seems to be broken. It’s logging undefined every time.