Introduction

WebSocket close code 1006 means the connection was closed abnormally without a proper close frame exchange. Unlike codes 1000 (normal) or 1001 (going away), code 1006 indicates the connection was interrupted - by a network failure, proxy timeout, server crash, or client crash. The application cannot distinguish between a clean close and a dropped connection without checking for this code.

Symptoms

  • WebSocket connection closed with code 1006
  • unexpected server response: 502 followed by 1006 close
  • Clients disconnect randomly without application-level reason
  • Reconnection storms after network blip
  • Load balancer dropping idle connections
  • ws.on('close', (code) => console.log(code)) shows 1006
javascript
ws.on('close', (code, reason) => {
    console.log(`WebSocket closed: code=${code}, reason=${reason.toString()}`);
    // code=1006, reason=<Buffer >  (empty - no reason provided)
    // 1006 means "abnormal closure" - no close frame was received
});

Common Causes

  • Network interruption between client and server
  • Load balancer or proxy closing idle connections
  • Server process restart without sending close frame
  • Client browser tab closed or navigated away
  • WebSocket ping/pong not configured (connection appears dead)
  • Firewall/NAT dropping long-lived connections

Step-by-Step Fix

  1. 1.Implement heartbeat with ping/pong:
  2. 2.```javascript
  3. 3.const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => { ws.isAlive = true;

ws.on('pong', () => { ws.isAlive = true; // Client responded });

ws.on('message', (data) => { // Handle messages }); });

// Check heartbeat every 30 seconds const interval = setInterval(() => { wss.clients.forEach((ws) => { if (ws.isAlive === false) { console.log('Client not responding to ping, terminating'); return ws.terminate(); // Force close } ws.isAlive = false; ws.ping(); // Send ping - expect pong response }); }, 30000);

wss.on('close', () => clearInterval(interval)); ```

  1. 1.Client-side reconnection with exponential backoff:
  2. 2.```javascript
  3. 3.function connectWithRetry(url, maxRetries = 10) {
  4. 4.let retries = 0;

function connect() { const ws = new WebSocket(url);

ws.on('open', () => { console.log('Connected'); retries = 0; // Reset on successful connection });

ws.on('close', (code) => { console.log(Disconnected: code=${code});

if (code === 1006 || code === 1001) { // Abnormal close or server going away - reconnect if (retries < maxRetries) { const delay = Math.min(1000 * Math.pow(2, retries), 30000); const jitter = Math.random() * 1000; console.log(Reconnecting in ${delay + jitter}ms (attempt ${retries + 1}));

setTimeout(() => { retries++; connect(); }, delay + jitter); } else { console.error('Max reconnection attempts reached'); } } });

ws.on('error', (err) => { console.error('WebSocket error:', err.message); });

return ws; }

return connect(); }

// Usage const ws = connectWithRetry('ws://localhost:8080'); ```

  1. 1.Configure proxy/load balancer timeouts:
  2. 2.```nginx
  3. 3.# Nginx configuration for WebSocket
  4. 4.location /ws {
  5. 5.proxy_pass http://backend;
  6. 6.proxy_http_version 1.1;
  7. 7.proxy_set_header Upgrade $http_upgrade;
  8. 8.proxy_set_header Connection "upgrade";

# Increase timeouts for long-lived WebSocket connections proxy_read_timeout 86400s; # 24 hours proxy_send_timeout 86400s; proxy_connect_timeout 60s;

# Send periodic proxy-level pings proxy_socket_keepalive on; } ```

  1. 1.Handle 1006 in application logic:
  2. 2.```javascript
  3. 3.class WebSocketClient {
  4. 4.constructor(url) {
  5. 5.this.url = url;
  6. 6.this.ws = null;
  7. 7.this.listeners = new Map();
  8. 8.this.connect();
  9. 9.}

connect() { this.ws = new WebSocket(this.url);

this.ws.on('open', () => { this.emit('open'); });

this.ws.on('message', (data) => { this.emit('message', data); });

this.ws.on('close', (code, reason) => { this.emit('close', code, reason);

// 1006 = abnormal - likely network issue if (code === 1006) { console.warn('Abnormal closure - possible network issue'); } });

this.ws.on('error', (err) => { this.emit('error', err); }); }

on(event, callback) { if (!this.listeners.has(event)) { this.listeners.set(event, []); } this.listeners.get(event).push(callback); }

emit(event, ...args) { const callbacks = this.listeners.get(event) || []; callbacks.forEach(cb => cb(...args)); } } ```

Prevention

  • Always implement ping/pong heartbeat on the server
  • Use exponential backoff with jitter for reconnection
  • Configure load balancer and proxy timeouts for WebSocket connections
  • Monitor WebSocket connection count and drop rate in production
  • Set clientTracking: true on ws.Server to track connected clients
  • Use ws.terminate() instead of ws.close() for unresponsive clients
  • Add connection-level logging to diagnose 1006 patterns
  • In Kubernetes, configure readinessProbe and livenessProbe separately from WebSocket health