[PR #911] [MERGED] fix(dev): swallow socket errors so peer disconnects don't crash dev server #941

Closed
opened 2026-05-06 13:10:59 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/911
Author: @james-elicx
Created: 4/26/2026
Status: Merged
Merged: 4/26/2026
Merged by: @james-elicx

Base: mainHead: claude/condescending-agnesi-dc8089


📝 Commits (1)

  • 591f59d fix(dev): swallow socket errors so peer disconnects don't crash dev server

📊 Changes

1 file changed (+12 additions, -0 deletions)

View changed files

📝 packages/vinext/src/index.ts (+12 -0)

📄 Description

Summary

Closes #905.

bun dev crashes intermittently with an unhandled ECONNRESET on a Socket when files are saved mid-request — typically when HMR triggers a reload while an RSC stream is still flushing. The trace has no vinext or app frames; it comes straight from Node internals. Node's default behavior on an unhandled 'error' event is to throw, and the dev server doesn't currently attach a listener on the request/response sockets, so a peer disconnect during res.write takes the process down.

This patch attaches a no-op 'error' listener on every connection in configureServer. The error is still surfaced to the writing stream as a normal write failure — the listener only neutralises Node's default throw, it doesn't hide real bugs.

Connection-level (rather than per-request) was chosen because vinext doesn't own all the dev middlewares: App Router routing goes through @vitejs/plugin-rsc, which streams its own response. One guard at the connection level covers both routers and any future streaming surface.

Dev-only — production runs as a Cloudflare Worker where the runtime owns socket lifecycle, and prod-server.ts already attaches an error handler on the request stream where it needs one.

Prior art

Both Vite and Next.js attach the same kind of guard for the same reason:

Test plan

  • vp check passes (format, lint, typecheck)
  • Manual repro from #905: bun dev, navigate around the example app, save source files rapidly while the browser is mid-RSC-stream — server no longer crashes with Unhandled 'error' event / ECONNRESET
  • HMR still functions normally after a forced disconnect (browser reloads, page renders)

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/911 **Author:** [@james-elicx](https://github.com/james-elicx) **Created:** 4/26/2026 **Status:** ✅ Merged **Merged:** 4/26/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `claude/condescending-agnesi-dc8089` --- ### 📝 Commits (1) - [`591f59d`](https://github.com/cloudflare/vinext/commit/591f59d552ac0f0194a87de7f6dceb9e02fef611) fix(dev): swallow socket errors so peer disconnects don't crash dev server ### 📊 Changes **1 file changed** (+12 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/index.ts` (+12 -0) </details> ### 📄 Description ## Summary Closes #905. `bun dev` crashes intermittently with an unhandled `ECONNRESET` on a `Socket` when files are saved mid-request — typically when HMR triggers a reload while an RSC stream is still flushing. The trace has no vinext or app frames; it comes straight from Node internals. Node's default behavior on an unhandled `'error'` event is to throw, and the dev server doesn't currently attach a listener on the request/response sockets, so a peer disconnect during `res.write` takes the process down. This patch attaches a no-op `'error'` listener on every connection in `configureServer`. The error is still surfaced to the writing stream as a normal write failure — the listener only neutralises Node's default throw, it doesn't hide real bugs. Connection-level (rather than per-request) was chosen because vinext doesn't own all the dev middlewares: App Router routing goes through `@vitejs/plugin-rsc`, which streams its own response. One guard at the connection level covers both routers and any future streaming surface. Dev-only — production runs as a Cloudflare Worker where the runtime owns socket lifecycle, and `prod-server.ts` already attaches an error handler on the request stream where it needs one. ## Prior art Both Vite and Next.js attach the same kind of guard for the same reason: - **Next.js** — pure no-op on `req`/`res` per request: [router-server.ts#L289-L294](https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-server.ts#L289-L294) - **Next.js** — same on the WebSocket upgrade path: [router-server.ts#L823-L830](https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-server.ts#L823-L830) - **Vite** — log-only handler on the HMR WebSocket: [ws.ts#L311](https://github.com/vitejs/vite/blob/main/packages/vite/src/node/server/ws.ts#L311) ## Test plan - [ ] `vp check` passes (format, lint, typecheck) - [ ] Manual repro from #905: `bun dev`, navigate around the example app, save source files rapidly while the browser is mid-RSC-stream — server no longer crashes with `Unhandled 'error' event` / `ECONNRESET` - [ ] HMR still functions normally after a forced disconnect (browser reloads, page renders) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:10:59 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#941
No description provided.