-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Summary
The @cloudflare/vite-plugin crashes with an unhandled socket error when a WebSocket client disconnects abruptly during active communication.
Error
node:events:486
throw er; // Unhandled 'error' event
^
Error: read ECONNRESET
at TCP.onStreamRead (node:internal/stream_base_commons:216:20)
Emitted 'error' event on Socket instance at:
at emitErrorNT (node:internal/streams/destroy:170:8)
at emitErrorCloseNT (node:internal/streams/destroy:129:3)
at process.processTicksAndRejections (node:internal/process/task_queues:89:21) {
errno: -54,
code: 'ECONNRESET',
syscall: 'read'
}
Root Cause
The crash happens in the WebSocket handling code in @cloudflare/vite-plugin. The coupleWebSocket function from miniflare couples a Node.js WebSocket to a workerd WebSocket, but when the connection is abruptly closed, the socket emits an 'error' event that isn't handled.
From @cloudflare/vite-plugin/dist/index.mjs:
import { coupleWebSocket } from 'miniflare';
// In handleWebSocket function:
httpServer.on('upgrade', async (request, socket, head) => {
// ...
nodeWebSocket.handleUpgrade(request, socket, head, async (clientWebSocket) => {
coupleWebSocket(clientWebSocket, workerWebSocket); // No error handling
nodeWebSocket.emit('connection', clientWebSocket, request);
});
});Neither the socket nor clientWebSocket has an error handler attached, so when ECONNRESET occurs, Node.js throws.
Reproduction
Minimal repro repo: https://github.com/schickling-repros/livestore-sync-crash-repro
git clone https://github.com/schickling-repros/livestore-sync-crash-repro
cd livestore-sync-crash-repro
npm install
# Terminal 1: Start Vite (with Durable Object sync backend)
npm run dev
# Terminal 2: Run stress test (rapidly connects/disconnects WebSockets)
npm run stressThe stress test creates many WebSocket connections to a Durable Object, syncs briefly, then closes. The crash is non-deterministic but usually happens within 50-200 connections.
# Increase iterations if needed
TOTAL_STORES=500 SYNC_WAIT_MS=20 npm run stressExpected Behavior
The Vite dev server should gracefully handle client disconnections (ECONNRESET) without crashing. The error should be caught and logged, not thrown as an unhandled exception.
Suggested Fix
Add error handlers to the sockets in either:
coupleWebSocketin miniflare - handle errors on the coupled socketshandleWebSocketin vite-plugin - add.on('error', ...)to socket/clientWebSocket
Example:
socket.on('error', (err) => {
if (err.code === 'ECONNRESET') {
// Client disconnected, clean up gracefully
return;
}
console.error('WebSocket error:', err);
});Environment
- @cloudflare/vite-plugin: ^1.1.0
- miniflare: (bundled with vite-plugin)
- vite: ^6.3.3
- Node.js: v24.12.0
- macOS
Context
This was discovered while using LiveStore's sync backend (Durable Objects) with rapid store creation/teardown during a backfill operation. Originally filed as livestorejs/livestore#982 before identifying miniflare as the source.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status