TanStack / TanStack/router

Throwing error in route loader with SSR streaming crashes dev server

Open
#6,200 9 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

information needed needed-for-start-stable package: solid-start
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 fetchQuery or ensureQueryData in 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.