How to handle WebSocket server errors and unexpected disconnects
server-level error event, per-client error handling, ECONNRESET, graceful server shutdown, wss.close callback, draining clients, process signal handling, health check endpoint
Two Layers of Error Handling
Errors fire at two levels. Always handle both:
// Server-level errors (port in use, etc.) wss.on('error', (err) => { console.error('Server error:', err.message); }); // Per-client errors wss.on('connection', (ws) => { ws.on('error', (err) => { // ECONNRESET is common — client closed without close frame if (err.code !== 'ECONNRESET') { console.error('Client error:', err.message); } }); });
Without a per-client error listener, an ECONNRESET on any client crashes the Node process. Always attach it.
Graceful Server Shutdown
process.on('SIGTERM', () => { wss.close(() => { console.log('All clients disconnected'); process.exit(0); }); // Force-close clients that do not disconnect for (const client of wss.clients) { client.terminate(); } });
wss.close() stops accepting new connections but waits for existing ones. Call client.terminate() on all clients to force immediate teardown during deploys. Add a health check HTTP endpoint that returns 200 while the server is healthy — load balancers use this to determine when to stop routing traffic before a restart.
