Which project does this relate to?
Start
Describe the bug
With @tanstack/nitro-v2-vite-plugin on Vite 6, a client that aborts a request while SSR is still streaming leaves router-core's SSR transform stream uncleaned: the abort never propagates to the transform's cleanup, so serialization never finishes. 120 seconds later the stream-lifetime watchdog in transformStreamWithRouter calls controller.error(...) — but by then the adapting node Readable has no consumer and no 'error' listener, so the entire dev process exits with an unhandled 'error' event:
SSR stream transform exceeded maximum lifetime (120000ms), forcing cleanup
node:events:485
throw er; // Unhandled 'error' event
^
Error: Stream lifetime exceeded
at Timeout.<anonymous> (.../@tanstack/router-core/dist/esm/ssr/transformStreamWithRouter.js:478:32)
Emitted 'error' event on Readable instance at:
at emitErrorNT (node:internal/streams/destroy:170:8)
error: script "dev" exited with code 1
This bites constantly in real usage: defaultPreload: "intent" aborts hover preloads all the time, so on any app with slow-ish SSR the dev server dies ~2 minutes after ordinary navigation.
What I've isolated:
- A fresh
create-start-app scaffold (Vite 8 + the new nitro/vite plugin, router-core 1.171.14) does not reproduce — the abort propagates, the transform is cleaned up immediately, and the watchdog never fires.
- The watchdog code in
transformStreamWithRouter is identical between router-core 1.171.13 and 1.171.14, so the difference is the dev-server adapter chain: the abort→cleanup propagation hole is specific to the @tanstack/nitro-v2-vite-plugin pipeline.
- The production build (
node .output/server/index.mjs, node-server preset) tolerates the same error — this is dev-only.
Two possible fixes, not mutually exclusive: propagate request aborts to the transform-stream cleanup in the nitro-v2 dev pipeline, and/or harden the watchdog in router-core to not error() a stream that no longer has a consumer (which would contain the blast radius for any adapter that misses cancellation).
Complete minimal reproducer
https://github.com/jayscambler/tanstack-start-ssr-abort-repro
(Three tiny routes; /slow has a 3s loader so a curl abort reliably lands mid-SSR.)
Steps to Reproduce the Bug
bun install && bun run dev (vite dev, port 3100)
curl -s --max-time 0.5 http://localhost:3100/slow — aborts while the 3s loader is still running
- Wait ~150 seconds
curl http://localhost:3100/ — the dev process has exited with code 1 (crash above in the terminal)
Expected behavior
An aborted request cleans up its SSR transform stream (as the Vite 8 + nitro/vite pipeline does), and in no case should a watchdog firing on an abandoned stream take down the whole dev server.
Screenshots or Videos
No response
Platform
- OS: macOS 15 (also observed on the bun preset)
- @tanstack/react-start: 1.168.26
- @tanstack/react-router: 1.170.16
- @tanstack/router-core: 1.171.13 (watchdog identical in 1.171.14)
- @tanstack/nitro-v2-vite-plugin: 1.155.0
- vite: 6.3.5, node: 23.11.0
Which project does this relate to?
Start
Describe the bug
With
@tanstack/nitro-v2-vite-pluginon Vite 6, a client that aborts a request while SSR is still streaming leaves router-core's SSR transform stream uncleaned: the abort never propagates to the transform's cleanup, so serialization never finishes. 120 seconds later the stream-lifetime watchdog intransformStreamWithRoutercallscontroller.error(...)— but by then the adapting nodeReadablehas no consumer and no'error'listener, so the entire dev process exits with an unhandled'error'event:This bites constantly in real usage:
defaultPreload: "intent"aborts hover preloads all the time, so on any app with slow-ish SSR the dev server dies ~2 minutes after ordinary navigation.What I've isolated:
create-start-appscaffold (Vite 8 + the newnitro/viteplugin, router-core 1.171.14) does not reproduce — the abort propagates, the transform is cleaned up immediately, and the watchdog never fires.transformStreamWithRouteris identical between router-core 1.171.13 and 1.171.14, so the difference is the dev-server adapter chain: the abort→cleanup propagation hole is specific to the@tanstack/nitro-v2-vite-pluginpipeline.node .output/server/index.mjs, node-server preset) tolerates the same error — this is dev-only.Two possible fixes, not mutually exclusive: propagate request aborts to the transform-stream cleanup in the nitro-v2 dev pipeline, and/or harden the watchdog in router-core to not
error()a stream that no longer has a consumer (which would contain the blast radius for any adapter that misses cancellation).Complete minimal reproducer
https://github.com/jayscambler/tanstack-start-ssr-abort-repro
(Three tiny routes;
/slowhas a 3s loader so a curl abort reliably lands mid-SSR.)Steps to Reproduce the Bug
bun install && bun run dev(vite dev, port 3100)curl -s --max-time 0.5 http://localhost:3100/slow— aborts while the 3s loader is still runningcurl http://localhost:3100/— the dev process has exited with code 1 (crash above in the terminal)Expected behavior
An aborted request cleans up its SSR transform stream (as the Vite 8 +
nitro/vitepipeline does), and in no case should a watchdog firing on an abandoned stream take down the whole dev server.Screenshots or Videos
No response
Platform