mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[PR #913] [MERGED] fix(dev): backstop uncaughtException for socket errors pipe() misses #943
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#943
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/cloudflare/vinext/pull/913
Author: @james-elicx
Created: 4/26/2026
Status: ✅ Merged
Merged: 4/26/2026
Merged by: @james-elicx
Base:
main← Head:claude/laughing-proskuriakova-02bf3d📝 Commits (2)
0d6c8b7fix(dev): backstop uncaughtException for socket disconnects pipe() misses95cd980fix: address bonk review on uncaughtException backstop📊 Changes
1 file changed (+38 additions, -0 deletions)
View changed files
📝
packages/vinext/src/index.ts(+38 -0)📄 Description
Summary
Follow-up to #911. The connection-level guard installed there fixes the direct
res.socketcase but doesn't cover Node'spipe()re-emission path. When aReadablesource errors and the destination has no'error'listener,pipe()emits the source's error onto the destination — and the destination isn't always the inbound connection socket.Real call sites in vinext that hit this:
fromWeb(fetch().body).pipe(res)inproxyExternalRewriteNode— source is the upstream fetch body stream, not an inbound socket.@vitejs/plugin-rscthat own their own pipe topology.fetch()from middleware — these never fire'connection'onserver.httpServer, so they sit entirely outside the existing guard.Stack trace from the field (
vp dlx vinext@54c91f1 dev, after a successfulGET /stage/oliver):Socket.onerror at internal/streams/readable:1035ispipe()'s internal error-propagation function — same crash shape that #911 set out to fix, different installation site.Approach
Add a process-level
uncaughtExceptionhandler scoped to the dev server. It drops only peer-disconnect codes (ECONNRESET,EPIPE,ECONNABORTED) and re-throws everything else onnextTick, which preserves Node's default crash semantics for real bugs. The handler is removed onhttpServer'close'so it doesn't leak across restarts.Why a process-level backstop:
fetch()and pipe re-emissions to non-socket destinations.configureServer, never installed in build/prod.Refs
Test plan
vp checkpasses (note: 12 pre-existing typecheck errors about missing peer deps are unchanged)proxyExternalRewriteNodein play: dev server no longer crashes after browser disconnect mid-streamhttpServer.close()followed by anotherlisten()doesn't double-register the handler🤖 Generated with Claude Code
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.