Skip to content

Commit d1b2ed0

Browse files
fix: don't crash the server when a streaming response is aborted
The generated SSR handler piped render output straight into the ReadableStream controller. Enqueueing after the client aborts (page reload, navigation away) throws ERR_INVALID_STATE — and streamed fragments write seconds after the shell, so a mid-stream abort took down the whole Node process from writeTasks. Guard writes behind a closed flag, absorb the close race in end(), and mark closure on cancel(). Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7a98823 commit d1b2ed0

2 files changed

Lines changed: 13 additions & 7 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"vite-plugin-solid": patch
3+
---
4+
5+
The generated SSR handler no longer crashes the server process when a client aborts a streaming response mid-flight. Enqueueing into a closed `ReadableStream` controller throws, and a streamed fragment can land seconds after the shell — an abort (page reload, navigation away) during that window took down the whole Node process with `ERR_INVALID_STATE`. Writes are now dropped once the stream closes or is cancelled.

src/ssr/index.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -478,18 +478,19 @@ export function ssrServe(
478478
` return new Response('<!DOCTYPE html>' + transform(result), { status, headers });`,
479479
` }`,
480480
` const encoder = new TextEncoder();`,
481+
// Streamed fragments write long after the shell; enqueueing after the
482+
// client aborts throws, which would crash the server. Drop late writes.
483+
` let closed = false;`,
481484
` const stream = new ReadableStream({`,
482485
` start(controller) {`,
483-
` controller.enqueue(encoder.encode('<!DOCTYPE html>'));`,
486+
` const enqueue = v => { if (!closed) try { controller.enqueue(encoder.encode(v)); } catch { closed = true; } };`,
487+
` enqueue('<!DOCTYPE html>');`,
484488
` result.pipe({`,
485-
` write(chunk) {`,
486-
` controller.enqueue(encoder.encode(transform(chunk)));`,
487-
` },`,
488-
` end() {`,
489-
` controller.close();`,
490-
` },`,
489+
` write(chunk) { enqueue(transform(chunk)); },`,
490+
` end() { if (!closed) { closed = true; try { controller.close(); } catch {} } },`,
491491
` });`,
492492
` },`,
493+
` cancel() { closed = true; },`,
493494
` });`,
494495
` return new Response(stream, { status, headers });`,
495496
`}`,

0 commit comments

Comments
 (0)