Skip to content

fix(router): await server$ stream close so a client disconnect can't crash Bun - #8798

Merged
wmertens merged 3 commits into
QwikDev:build/v2from
aggyomfg:fix/v2-server-function-stream-close
Jul 14, 2026
Merged

wmertens merged 3 commits into
QwikDev:build/v2from
aggyomfg:fix/v2-server-function-stream-close

Conversation

@aggyomfg

@aggyomfg aggyomfg commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

What is it?

  • Bug

Description

A streaming server$ (an async function* consumed as a stream) crashes the
entire Bun process when the client disconnects mid-stream (SPA navigation
away, tab close, refresh, network drop).

When the client goes away, the response writable is errored, so write/close
reject. The async-iterator branch of runServerFunction called stream.close()
without awaiting or catching it, so the rejection went unhandled. On Bun the
default unhandledRejection behavior is to abort the process (Node only logs a
warning, which is why this stayed hidden there). After the crash every following
request fails with ECONNREFUSED — the server is gone.

The fix extracts the streaming branch into streamServerFunctionResult and
awaits + swallows both the per-item write and the final close, so a
disconnected writable tears down quietly instead of leaking a rejection.

Reproduction: a streaming server$ on the Bun adapter — open the stream,
read one chunk, then abort the request while it is still producing; the process
exits.

Related: #5929 (fixed the TextEncoderStream polyfill, not this close),
#4381 (Node ERR_STREAM_DESTROYED on the same class of teardown),
oven-sh/bun#18228 (Bun's stance: the framework must catch the close).

Test drives streamServerFunctionResult with a writable whose close() rejects (the
post-disconnect state) and asserts no unhandled rejection escapes. It is red
without the fix (leaks Cannot close a writable stream that is closed or errored) and green with it. The process-abort half is Bun-specific and can't be
reproduced under Node, but the leaked-rejection root cause is covered here.

Checklist

  • My code follows the developer guidelines of this project
  • I performed a self-review of my own code
  • I added a changeset with pnpm change
  • I added new tests to cover the fix / functionality

@aggyomfg
aggyomfg requested a review from a team as a code owner July 4, 2026 08:42
@changeset-bot

changeset-bot Bot commented Jul 4, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 55638e4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@qwik.dev/router Patch
eslint-plugin-qwik Patch
@qwik.dev/core Patch
create-qwik Patch
@qwik.dev/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

break;
}
// Swallow rejection: writable may be errored by a client disconnect.
await stream.write(encoder.encode(`${message}\n`)).catch(() => {});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we log the error message in dev mode at least?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added dev-logs.

}
}

// Streams an async-iterator server function result to the response writable. A mid-stream

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

@pkg-pr-new

pkg-pr-new Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@qwik.dev/core

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/core@8798

@qwik.dev/router

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/router@8798

eslint-plugin-qwik

npm i https://pkg.pr.new/QwikDev/qwik/eslint-plugin-qwik@8798

create-qwik

npm i https://pkg.pr.new/QwikDev/qwik/create-qwik@8798

@qwik.dev/optimizer

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/optimizer@8798

@qwik.dev/devtools

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/devtools@8798

commit: 55638e4

@github-actions

github-actions Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor
built with Refined Cloudflare Pages Action

⚡ Cloudflare Pages Deployment

Name Status Preview Last Commit
qwik-docs ✅ Ready (View Log) Visit Preview 40d2fc2

@wmertens wmertens left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@wmertens
wmertens enabled auto-merge July 13, 2026 06:34
@wmertens

Copy link
Copy Markdown
Member

@aggyomfg the unit test is failing on an undefined qrl

auto-merge was automatically disabled July 13, 2026 15:13

Head branch was pushed to by a user without write access

@aggyomfg

Copy link
Copy Markdown
Contributor Author

@aggyomfg the unit test is failing on an undefined qrl

fixed in 55638e4

@wmertens
wmertens enabled auto-merge July 14, 2026 12:56
@wmertens
wmertens merged commit e1bb72f into QwikDev:build/v2 Jul 14, 2026
48 checks passed
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.

3 participants