Skip to content

WebSocket Close Codes Reference

WebSocket connections can close for various reasons, each identified by a specific close code. Understanding these codes is crucial for debugging connection issues and implementing robust error handling in your WebSocket applications.

Quick Reference Table

Standard Close Codes (1000-1015)

CodeNameDescriptionWhen to Use
1000Normal ClosureConnection fulfilled its purposeStandard graceful shutdown
1001Going AwayServer/browser is shutting downServer maintenance, page navigation
1002Protocol ErrorProtocol violation detectedInvalid frame, bad data format
1003Unsupported DataReceived incompatible data typeWrong message format received
1004ReservedReserved for future useDo not use
1005No Status ReceivedExpected status code was absentInternal use only (cannot send)
1006Abnormal ClosureConnection lost unexpectedlyInternal use only (cannot send)
1007Invalid Payload DataMessage data doesn’t match typeMalformed UTF-8, invalid JSON
1008Policy ViolationGeneric policy violationRate limiting, message size exceeded
1009Message Too BigMessage exceeds size limitsPayload too large
1010Mandatory ExtensionRequired extension not supportedExtension negotiation failed
1011Internal ErrorServer encountered unexpected errorServer-side exception
1012Service RestartServer is restartingPlanned restart
1013Try Again LaterTemporary server overloadServer temporarily unavailable
1014Bad GatewayGateway/proxy received invalid responseProxy server issues
1015TLS HandshakeTLS/SSL handshake failureInternal use only (cannot send)

Reserved Code Ranges

RangePurposeUsage
0-999ReservedNot used, invalid if received
1000-2999WebSocket ProtocolStandard codes and future extensions
3000-3999Libraries/FrameworksFramework-specific codes
4000-4999Private UseApplication-specific codes

Detailed Code Explanations

1000: Normal Closure

The connection successfully completed its purpose and is closing normally.

Common Scenarios:

  • User logs out
  • Chat session ends
  • File transfer completes
  • Client explicitly closes connection

Troubleshooting:

  • This is the expected code for graceful shutdowns
  • No action needed unless unexpected
// Client-side closing
ws.close(1000, "Work complete");
// Handling normal closure
ws.onclose = (event) => {
if (event.code === 1000) {
console.log("Connection closed normally");
}
};

1001: Going Away

The endpoint is going away, either due to server shutdown or browser navigation.

Common Scenarios:

  • Server scheduled maintenance
  • Browser tab closing
  • Page navigation
  • Application shutdown

Troubleshooting:

  • Implement reconnection logic with backoff
  • Save application state before closure
  • Notify users of maintenance windows
// Browser navigation detection
window.addEventListener('beforeunload', () => {
ws.close(1001, "Page unloading");
});
// Reconnection logic
ws.onclose = (event) => {
if (event.code === 1001) {
setTimeout(() => {
reconnect();
}, 5000);
}
};

1002: Protocol Error

A WebSocket protocol violation was detected.

Common Scenarios:

  • Invalid frame structure
  • Incorrect masking
  • Bad opcode
  • Protocol version mismatch

Troubleshooting:

  • Verify WebSocket library versions
  • Check for proxy interference
  • Validate frame construction
  • Ensure proper protocol implementation

1003: Unsupported Data

The endpoint received data it cannot accept.

Common Scenarios:

  • Binary data sent to text-only endpoint
  • Text data sent to binary-only endpoint
  • Unsupported message format

Implementation:

ws.onmessage = (event) => {
if (event.data instanceof Blob && !supportsBinary) {
ws.close(1003, 'Binary data not supported');
}
};

1006: Abnormal Closure

Connection was closed abnormally without a close frame.

Common Scenarios:

  • Network failure
  • Client crash
  • Proxy timeout
  • Firewall intervention

Important: This code cannot be sent in a close frame; it’s only for the receiving endpoint to indicate abnormal closure.

Troubleshooting:

  • Implement heartbeat/ping-pong mechanism
  • Add connection timeout detection
  • Use exponential backoff for reconnection
// Detecting abnormal closure
ws.onclose = (event) => {
if (event.code === 1006) {
console.error('Connection lost unexpectedly');
// Implement reconnection strategy
reconnectWithBackoff();
}
};
// Heartbeat mechanism to prevent 1006
const heartbeat = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }));
}
}, 30000);

1007: Invalid Payload Data

The message data doesn’t match the declared type.

Common Scenarios:

  • Invalid UTF-8 in text message
  • Malformed JSON
  • Schema validation failure
  • Encoding errors

Prevention:

// Validate before sending
function sendMessage(data) {
try {
const json = JSON.stringify(data);
// Validate UTF-8 encoding
if (!isValidUTF8(json)) {
throw new Error('Invalid UTF-8');
}
ws.send(json);
} catch (error) {
console.error('Invalid payload:', error);
}
}

1008: Policy Violation

Generic status for policy violations when no other code applies.

Common Scenarios:

  • Rate limiting exceeded
  • Authentication failure
  • Authorization denied
  • Terms of service violation

Implementation:

// Server-side rate limiting
const connectionCounts = new Map();
wss.on('connection', (ws, req) => {
const ip = req.socket.remoteAddress;
const count = connectionCounts.get(ip) || 0;
if (count >= MAX_CONNECTIONS_PER_IP) {
ws.close(1008, 'Connection limit exceeded');
return;
}
connectionCounts.set(ip, count + 1);
});

1009: Message Too Big

Message exceeds the maximum size the endpoint can handle.

Common Scenarios:

  • File upload too large
  • Batch message exceeds limit
  • Accumulated frame size too large

Prevention:

// Check message size before sending
const MAX_MESSAGE_SIZE = 1024 * 1024; // 1MB
function sendLargeData(data) {
const message = JSON.stringify(data);
if (message.length > MAX_MESSAGE_SIZE) {
// Split into chunks
const chunks = chunkMessage(message, MAX_MESSAGE_SIZE);
chunks.forEach((chunk) => ws.send(chunk));
} else {
ws.send(message);
}
}

1011: Internal Error

The server encountered an unexpected condition preventing request fulfillment.

Common Scenarios:

  • Unhandled server exception
  • Database connection failure
  • Critical service unavailable
  • Memory allocation failure

Server Implementation:

ws.on('message', async (data) => {
try {
await processMessage(data);
} catch (error) {
console.error('Internal error:', error);
ws.close(1011, 'Internal server error');
}
});

Application-Specific Codes (4000-4999)

You can define custom close codes for application-specific scenarios:

// Custom close codes
const CustomCodes = {
SESSION_EXPIRED: 4001,
DUPLICATE_CONNECTION: 4002,
MAINTENANCE_MODE: 4003,
SUBSCRIPTION_EXPIRED: 4004,
BANNED_USER: 4005,
};
// Usage
if (sessionExpired) {
ws.close(CustomCodes.SESSION_EXPIRED, 'Session expired');
}
// Handling custom codes
ws.onclose = (event) => {
switch (event.code) {
case CustomCodes.SESSION_EXPIRED:
redirectToLogin();
break;
case CustomCodes.DUPLICATE_CONNECTION:
showDuplicateConnectionWarning();
break;
default:
handleStandardClose(event.code);
}
};

Best Practices

Error Handling Strategy

class WebSocketManager {
constructor(url) {
this.url = url;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 1000;
this.connect();
}
connect() {
this.ws = new WebSocket(this.url);
this.setupEventHandlers();
}
setupEventHandlers() {
this.ws.onclose = (event) => {
console.log(`Connection closed: ${event.code} - ${event.reason}`);
// Handle specific close codes
switch (event.code) {
case 1000: // Normal closure
console.log('Connection completed successfully');
break;
case 1006: // Abnormal closure
this.reconnect();
break;
case 1008: // Policy violation
this.handlePolicyViolation(event.reason);
break;
case 1011: // Internal error
this.notifyError('Server error occurred');
this.reconnect();
break;
default:
if (event.code >= 4000 && event.code <= 4999) {
this.handleCustomCode(event.code, event.reason);
} else {
this.reconnect();
}
}
};
}
reconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('Max reconnection attempts reached');
this.notifyError('Connection failed permanently');
return;
}
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);
console.log(`Reconnecting in ${delay}ms...`);
setTimeout(() => {
this.reconnectAttempts++;
this.connect();
}, delay);
}
handlePolicyViolation(reason) {
if (reason.includes('rate limit')) {
this.notifyError('Too many requests. Please slow down.');
setTimeout(() => this.reconnect(), 60000); // Wait 1 minute
} else if (reason.includes('authentication')) {
this.redirectToLogin();
}
}
handleCustomCode(code, reason) {
// Handle application-specific codes
console.log(`Custom close code ${code}: ${reason}`);
}
notifyError(message) {
// Notify UI of error
console.error(message);
}
redirectToLogin() {
window.location.href = '/login';
}
}

Logging and Monitoring

// Comprehensive close event logging
function logCloseEvent(event) {
const logEntry = {
timestamp: new Date().toISOString(),
code: event.code,
reason: event.reason || 'No reason provided',
wasClean: event.wasClean,
readyState: event.target.readyState,
url: event.target.url,
// Additional context
userAgent: navigator.userAgent,
connectionDuration: Date.now() - connectionStartTime,
messagesExchanged: messageCount,
};
// Send to monitoring service
sendToMonitoring(logEntry);
// Local logging
console.table(logEntry);
}
ws.onclose = logCloseEvent;

Testing Close Codes

Unit Testing Example

// Jest test example
describe('WebSocket Close Handling', () => {
let ws;
let mockServer;
beforeEach(() => {
mockServer = new WS.Server({ port: 8080 });
ws = new WebSocket('ws://localhost:8080');
});
afterEach(() => {
ws.close();
mockServer.close();
});
test('handles normal closure (1000)', (done) => {
ws.onopen = () => {
ws.close(1000, 'Test complete');
};
ws.onclose = (event) => {
expect(event.code).toBe(1000);
expect(event.reason).toBe('Test complete');
done();
};
});
test('handles message too big (1009)', (done) => {
mockServer.on('connection', (socket) => {
socket.close(1009, 'Message too large');
});
ws.onclose = (event) => {
expect(event.code).toBe(1009);
done();
};
});
});

Common Issues and Solutions

Issue: Frequent 1006 Errors

Symptoms: Connection drops without proper close frame

Solutions:

  1. Implement heartbeat mechanism
  2. Check proxy/firewall timeout settings
  3. Verify network stability
  4. Add connection state monitoring

Issue: 1002 Protocol Errors

Symptoms: Immediate disconnection after connection

Solutions:

  1. Verify WebSocket library compatibility
  2. Check for proxy/CDN interference
  3. Ensure proper Sec-WebSocket headers
  4. Validate frame construction

Issue: Rate Limiting (1008)

Symptoms: Connections rejected with policy violation

Solutions:

  1. Implement client-side throttling
  2. Use exponential backoff
  3. Batch messages when possible
  4. Monitor connection frequency

Browser Compatibility

Close CodeChromeFirefoxSafariEdge
1000-1003✅ Full✅ Full✅ Full✅ Full
1004-1007✅ Full✅ Full✅ Full✅ Full
1008-1011✅ Full✅ Full✅ Full✅ Full
1012-1015⚠️ Partial⚠️ Partial⚠️ Partial⚠️ Partial
4000-4999✅ Full✅ Full✅ Full✅ Full

Note: Codes 1005, 1006, and 1015 cannot be sent by applications; they’re only set by the browser/runtime.

Security Considerations

  1. Don’t leak sensitive information in close reasons
  2. Validate close codes from untrusted clients
  3. Rate limit connection attempts after certain close codes
  4. Log suspicious patterns of close codes for security monitoring
  5. Use custom codes for application-specific security events

Further Reading