Throwing error in route loader with SSR streaming crashes dev server
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15.1k
- Forks
- 1.9k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 143
Description
Which project does this relate to?
Start
Describe the bug
When streaming loader data during SSR, which is activated by neither awaiting nor returning the fetched data, an error thrown by the fetcher (e.g. Zod validation error) will crash the Vite dev server (with Nitro v3 plugin, Cloudflare Workers preset). Now if we don't enable streaming, then the error is properly caught by the default error component of the router, and the dev server doesn't crash.
Your Example Website or App
Not available yet, but very minimal code to reproduce locally, see below
Steps to Reproduce the Bug or Issue
Here's a minimal example to reproduce the scenario:
const ARTICLES_PER_PAGE = 0; // Invalid input according to Zod schema
export const Route = createFileRoute('/')({
loader: async ({ context }) => {
const data = context.queryClient.ensureQueryData(
articlesQueryOptions(0, ARTICLES_PER_PAGE),
);
// return data; // not returning gives us streaming behavior according docs
},
component: HomePage,
});
When we don't return (nor await) the data, opening the page immediately crashed the dev server. The server console prints the following:
[4:39:14 PM] ERROR Worker error: [
{
"origin": "number",
"code": "too_small",
"minimum": 1,
"inclusive": true,
"path": [
"limit"
],
"message": "Too small: expected number to be >=1"
}
]
at execValidator (node_modules/.pnpm/@tanstack+start-client-core@1.142.13/node_modules/@tanstack/start-client-core/src/createServerFn.ts:673:13)
at next (node_modules/.pnpm/@tanstack+start-client-core@1.142.13/node_modules/@tanstack/start-client-core/src/createServerFn.ts:196:24)
at executeMiddleware (node_modules/.pnpm/@tanstack+start-client-core@1.142.13/node_modules/@tanstack/start-client-core/src/createServerFn.ts:233:10)
at AsyncFunction.listPublicArticlesFn.__executeServer (node_modules/.pnpm/@tanstack+start-client-core@1.142.13/node_modules/@tanstack/start-client-core/src/createServerFn.ts:147:20)
at src/server/query.ts:238:10
at Object.fn [as extractedFn] (node_modules/.pnpm/@tanstack+start-server-core@1.142.13_crossws@0.4.1_srvx@0.9.7_/node_modules/@tanstack/start-server-core/src/createSsrRpc.ts:15:12)
at client (node_modules/.pnpm/@tanstack+start-client-core@1.142.13/node_modules/@tanstack/start-client-core/src/createServerFn.ts:705:21)
at queryFn (src/tsq.ts:36:11)
node:internal/deps/undici/undici:13510
Error.captureStackTrace(err);
^
TypeError: fetch failed
at node:internal/deps/undici/undici:13510:13
at async nitroDevMiddleware (node_modules/.pnpm/nitro@3.0.1-alpha.1_@libsql+client@0.15.15_drizzle-orm@0.45.0_@libsql+client@0.15.15_ky_639768f3d07319a3d1fe53facc673f3e/node_modules/nitro/dist/_build/vite.plugin.mjs:348:18) {
[cause]: SocketError: other side closed
at Socket.onHttpSocketEnd (node_modules/.pnpm/undici@7.16.0/node_modules/undici/lib/dispatcher/client-h1.js:904:22)
at Socket.emit (node:events:530:35)
at endReadableNT (node:internal/streams/readable:1698:12)
at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
code: 'UND_ERR_SOCKET',
socket: {
localAddress: undefined,
localPort: undefined,
remoteAddress: undefined,
remotePort: undefined,
remoteFamily: undefined,
timeout: undefined,
bytesWritten: 791,
bytesRead: 0
}
}
}
Node.js v22.18.0
ELIFECYCLE Command failed with exit code 1.
Now, as soon as we switch to returning the data, the dev server crash stops, and the browser renders the default ErrorComponent defined on the router.
Expected behavior
Ideally, turning on SSR streaming wouldn't make the (dev) server less robust than in non-streaming mode.
Screenshots or Videos
No response
Platform
- Router / Start Version: 1.142.13
- OS: macOS
- Browser: Chrome
- Browser Version: 144.0
- Bundler: Vite
- Bundler Version: 7.3.0
Additional context
Using "nitro": "3.0.1-alpha.1" with preset: 'cloudflare_module'
Relevant quote from docs:
You can also prefetch with
fetchQueryorensureQueryDatain a loader without consuming the data in a component. If you return the promise directly from the loader, it will be awaited and thus block the SSR request until the query finishes. If you don't await the promise nor return it, the query will be started on the server and will be streamed to the client without blocking the SSR request.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the streaming route-loader path described in the reproduction, then trace src/server/query.ts and src/tsq.ts alongside createServerFn.ts and Nitro's nitroDevMiddleware stack entries. Reproduce with ensureQueryData and the invalid Zod limit while the promise is neither awaited nor returned; done means the error reaches the router's default ErrorComponent without terminating the Vite dev server, with coverage for streaming and non-streaming behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript, vite
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100