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
Section titled “Quick Reference Table”Standard Close Codes (1000-1015)
Section titled “Standard Close Codes (1000-1015)”| Code | Name | Description | When to Use |
|---|---|---|---|
| 1000 | Normal Closure | Connection fulfilled its purpose | Standard graceful shutdown |
| 1001 | Going Away | Server/browser is shutting down | Server maintenance, page navigation |
| 1002 | Protocol Error | Protocol violation detected | Invalid frame, bad data format |
| 1003 | Unsupported Data | Received incompatible data type | Wrong message format received |
| 1004 | Reserved | Reserved for future use | Do not use |
| 1005 | No Status Received | Expected status code was absent | Internal use only (cannot send) |
| 1006 | Abnormal Closure | Connection lost unexpectedly | Internal use only (cannot send) |
| 1007 | Invalid Payload Data | Message data doesn’t match type | Malformed UTF-8, invalid JSON |
| 1008 | Policy Violation | Generic policy violation | Rate limiting, message size exceeded |
| 1009 | Message Too Big | Message exceeds size limits | Payload too large |
| 1010 | Mandatory Extension | Required extension not supported | Extension negotiation failed |
| 1011 | Internal Error | Server encountered unexpected error | Server-side exception |
| 1012 | Service Restart | Server is restarting | Planned restart |
| 1013 | Try Again Later | Temporary server overload | Server temporarily unavailable |
| 1014 | Bad Gateway | Gateway/proxy received invalid response | Proxy server issues |
| 1015 | TLS Handshake | TLS/SSL handshake failure | Internal use only (cannot send) |
Reserved Code Ranges
Section titled “Reserved Code Ranges”| Range | Purpose | Usage |
|---|---|---|
| 0-999 | Reserved | Not used, invalid if received |
| 1000-2999 | WebSocket Protocol | Standard codes and future extensions |
| 3000-3999 | Libraries/Frameworks | Framework-specific codes |
| 4000-4999 | Private Use | Application-specific codes |
Detailed Code Explanations
Section titled “Detailed Code Explanations”1000: Normal Closure
Section titled “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 closingws.close(1000, "Work complete");
// Handling normal closurews.onclose = (event) => { if (event.code === 1000) { console.log("Connection closed normally"); }};// Server-side closingws.close(1000, "Session ended");
// Handling closurews.on('close', (code, reason) => { if (code === 1000) { console.log(`Normal closure: ${reason}`); }});# Closing connectionawait websocket.close(code=1000, reason="Complete")
## Handling closure
try: await websocket.recv()except websockets.ConnectionClosedOK: print("Connection closed normally")// Sending close frameerr := conn.WriteMessage( websocket.CloseMessage, websocket.FormatCloseMessage(1000, "Done"),)
// Handling close_, _, err := conn.ReadMessage()if websocket.IsCloseError(err, 1000) { log.Println("Normal closure")}1001: Going Away
Section titled “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 detectionwindow.addEventListener('beforeunload', () => { ws.close(1001, "Page unloading");});
// Reconnection logicws.onclose = (event) => { if (event.code === 1001) { setTimeout(() => { reconnect(); }, 5000); }};// Graceful server shutdownprocess.on('SIGTERM', () => { wss.clients.forEach(client => { client.close(1001, "Server shutting down"); }); server.close();});1002: Protocol Error
Section titled “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
Section titled “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
Section titled “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 closurews.onclose = (event) => { if (event.code === 1006) { console.error('Connection lost unexpectedly'); // Implement reconnection strategy reconnectWithBackoff(); }};
// Heartbeat mechanism to prevent 1006const heartbeat = setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'ping' })); }}, 30000);1007: Invalid Payload Data
Section titled “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 sendingfunction 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
Section titled “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 limitingconst 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
Section titled “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 sendingconst 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
Section titled “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)
Section titled “Application-Specific Codes (4000-4999)”You can define custom close codes for application-specific scenarios:
// Custom close codesconst CustomCodes = { SESSION_EXPIRED: 4001, DUPLICATE_CONNECTION: 4002, MAINTENANCE_MODE: 4003, SUBSCRIPTION_EXPIRED: 4004, BANNED_USER: 4005,};
// Usageif (sessionExpired) { ws.close(CustomCodes.SESSION_EXPIRED, 'Session expired');}
// Handling custom codesws.onclose = (event) => { switch (event.code) { case CustomCodes.SESSION_EXPIRED: redirectToLogin(); break; case CustomCodes.DUPLICATE_CONNECTION: showDuplicateConnectionWarning(); break; default: handleStandardClose(event.code); }};Best Practices
Section titled “Best Practices”Error Handling Strategy
Section titled “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
Section titled “Logging and Monitoring”// Comprehensive close event loggingfunction 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
Section titled “Testing Close Codes”Unit Testing Example
Section titled “Unit Testing Example”// Jest test exampledescribe('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
Section titled “Common Issues and Solutions”Issue: Frequent 1006 Errors
Section titled “Issue: Frequent 1006 Errors”Symptoms: Connection drops without proper close frame
Solutions:
- Implement heartbeat mechanism
- Check proxy/firewall timeout settings
- Verify network stability
- Add connection state monitoring
Issue: 1002 Protocol Errors
Section titled “Issue: 1002 Protocol Errors”Symptoms: Immediate disconnection after connection
Solutions:
- Verify WebSocket library compatibility
- Check for proxy/CDN interference
- Ensure proper Sec-WebSocket headers
- Validate frame construction
Issue: Rate Limiting (1008)
Section titled “Issue: Rate Limiting (1008)”Symptoms: Connections rejected with policy violation
Solutions:
- Implement client-side throttling
- Use exponential backoff
- Batch messages when possible
- Monitor connection frequency
Browser Compatibility
Section titled “Browser Compatibility”| Close Code | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| 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
Section titled “Security Considerations”- Don’t leak sensitive information in close reasons
- Validate close codes from untrusted clients
- Rate limit connection attempts after certain close codes
- Log suspicious patterns of close codes for security monitoring
- Use custom codes for application-specific security events