Skip to content

fix: propagate client disconnects to response body streams#93906

Open
coffeeispower wants to merge 2 commits into
vercel:canaryfrom
coffeeispower:fix-client-disconnection
Open

fix: propagate client disconnects to response body streams#93906
coffeeispower wants to merge 2 commits into
vercel:canaryfrom
coffeeispower:fix-client-disconnection

Conversation

@coffeeispower
Copy link
Copy Markdown

@coffeeispower coffeeispower commented May 17, 2026

What?

Fixes response stream cancellation when the client disconnects before the response body finishes streaming.

Why?

Long-lived streaming responses need their body stream to be cancelled when the client connection is closed. Without that cancellation, application cleanup logic attached to the stream may not run, which can leave per-client resources alive after the client has gone away even if the app has registered a event listener using request.signal.addEventListener("abort", ...)

This was found while building Radiant, a TypeScript web app for creating radios that you can schedule playlists, songs, ad sections and other things and it serves long-lived ICY/audio streams from Next.js route handlers. Each listener connection owns stream resources and registers cleanup logic that must run when the client disconnects.

In the real reproduction case, connecting with mpv and then closing the player aborted the HTTP response, but the response body stream was not cancelled. The route returned, but the stream finalizer responsible for removing the listener from the radio runtime did not run until this piping path was patched to propagate the disconnect to the body stream.

This is not only specific to audio: the same issue can affect any long-lived response body stream that relies on cancellation/finalizers for cleanup, such as event streams, proxy streams, or custom streaming route handlers.

How?

The response piping path now propagates client aborts to the response body stream. It also avoids surfacing noisy double-close errors when the writable side has already been closed or errored during disconnect cleanup.

Comment thread packages/next/src/server/pipe-readable.ts
@belgattitude
Copy link
Copy Markdown
Contributor

@coffeeispower i faced the same issue. Just stumbled on an upstream fix in recent nodejs 24.16

nodejs/node#62541

do you think your fix would compete in an unexpected way with the one of node ?

@coffeeispower
Copy link
Copy Markdown
Author

coffeeispower commented May 27, 2026

@belgattitude I'm not running next.js on node.js, i'm running it on bun.js because I actually depend on some of its libraries like postgres and redis and Bun.file, Bun.exec and other functions, but i still was able to reproduce it in node.js too so I thought it was a next.js bug

@coffeeispower
Copy link
Copy Markdown
Author

coffeeispower commented May 27, 2026

But either way, it's better to be safe than sorry, if it double-closes the stream it will throw an exception but I already made it ignore the exception because it's not really useful and doesn't affect anything

@belgattitude
Copy link
Copy Markdown
Contributor

Make sense. Thanks for clarification.

I’ll link this PR #93938 to keep visibility as I’m interested by a resolution to this exact issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants