ws
semver
>=8.17.1postconditions17functions7last verified2026-04-16coverage score88%Postconditions — what we check
- WebSocket · missing-error-handlererrorWhenWebSocket instance created without error event handlerThrows
Emits 'error' event that crashes process if not handledRequired handlingCaller MUST attach error event handler immediately after creating WebSocket. Without error handler, unhandled 'error' events crash the entire Node.js process. CRITICAL: This is the #1 production bug (60% of codebases). Always add: ws.on('error', (error) => { handle_error(error); })costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - WebSocket · connection-errorerrorWhenConnection fails (network error, DNS failure, timeout, handshake failure)Throws
Emits 'error' event with Error objectRequired handlingCaller MUST handle connection errors via error event handler. Common errors: ECONNREFUSED, ENOTFOUND, ETIMEDOUT, ECONNRESET. Error event is emitted before close event. Implement reconnection logic with exponential backoff.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - WebSocket · protocol-violationerrorWhenWebSocket protocol violation (invalid frames, reserved bits set)Throws
Emits 'error' event and closes connection with code 1002Required handlingCaller MUST handle protocol errors. Usually indicates server or client implementation bug. Close code 1002 means protocol error. DO NOT RETRY - fix implementation.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[3] - WebSocket · missing-close-handlerwarningWhenConnection closes but no close event handler attachedThrows
Emits 'close' event with code and reasonRequired handlingCaller SHOULD handle close events for cleanup and reconnection. Close codes: 1000 (normal), 1006 (abnormal), 1009 (too big), etc. Code 1006 indicates abnormal close (connection lost) - SHOULD reconnect. Code 1000 indicates normal close - DO NOT reconnect. Remove event listeners in close handler to prevent memory leaks.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[4] - send · send-before-openerrorWhensend() called before connection is open (readyState !== OPEN)Throws
Error: WebSocket is not open: readyState X (CONNECTING|CLOSING|CLOSED)Required handlingCaller MUST check readyState before sending OR send only in open event. VERY COMMON BUG (50% of codebases): Sending immediately after new WebSocket(). CORRECT: ws.on('open', () => { ws.send(data); }) CORRECT: if (ws.readyState === WebSocket.OPEN) { ws.send(data); } WRONG: ws.send(data); // Immediately after new WebSocket() - crashes!costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[5] - send · backpressure-exceededwarningWhenSend buffer full (bufferedAmount exceeds threshold)Throws
Does NOT throw, but causes memory exhaustion and OOM crashesRequired handlingCaller MUST check bufferedAmount before sending in high-throughput scenarios. If bufferedAmount > threshold (e.g., 1 MB), pause sending. Wait for drain event before resuming. WITHOUT backpressure handling: memory leak, OOM crash. Example: if (ws.bufferedAmount > 1024*1024) { pause_sending(); }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[6] - send · message-too-largeerrorWhenMessage size exceeds maxPayloadThrows
RangeError: Invalid WebSocket frame: payload length > maxPayloadRequired handlingCaller MUST validate message size before sending. Configure maxPayload appropriately (default 100 MiB may be too high). Connection closes with code 1009 (message too big). Split large messages into chunks or use chunking protocol.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[7] - WebSocketServer · server-errorerrorWhenServer error (port in use, permission denied, etc.)Throws
Emits 'error' event on serverRequired handlingCaller MUST attach error event handler to server. Common errors: EADDRINUSE (port in use), EACCES (permission denied). Without handler, errors crash the process.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[8] - WebSocketServer · client-error-before-connectionwarningWhenClient error before connection established (handshake failure)Throws
Emits 'wsClientError' event on serverRequired handlingCaller SHOULD handle wsClientError for errors during handshake. Prevents crashes from malformed upgrade requests. This event is emitted BEFORE connection event.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[9] - WebSocketServer · no-origin-validationwarningWhenServer accepts connections without origin validationThrows
Does NOT throw, but allows cross-origin attacksRequired handlingCaller MUST validate origin in production via verifyClient callback. WITHOUT validation: any website can connect to your WebSocket server. SECURITY RISK: Cross-Site WebSocket Hijacking (CSWSH). Example: new WebSocketServer({ verifyClient: (info) => { return allowedOrigins.includes(info.origin); } });costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[10] - close · close-emits-eventwarningWhenConnection closes (gracefully or abnormally)Throws
Emits 'close' event with code and reasonRequired handlingCaller SHOULD handle close event for cleanup. Close codes indicate reason: 1000 (normal), 1006 (abnormal), etc. Code 1006 means connection lost without close frame (network issue). Code 1000 means clean shutdown. Code 1009 means message too big. Remove event listeners to prevent memory leaks.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[4] - ping · ping-before-openerrorWhenping() is called while the WebSocket is still in CONNECTING state (readyState === WebSocket.CONNECTING, i.e., before the 'open' event fires).Throws
Error: WebSocket is not open: readyState 0 (CONNECTING)Required handlingCaller MUST check readyState before calling ping(), or only send pings after the 'open' event. Heartbeat intervals MUST start in the 'open' event handler, NOT immediately after new WebSocket(). CORRECT heartbeat pattern: ws.on('open', () => { const heartbeat = setInterval(() => { if (ws.readyState === WebSocket.OPEN) ws.ping(); }, 30000); ws.on('close', () => clearInterval(heartbeat)); }); WRONG (crashes if called before connection is established): ws.ping(); // Throws immediatelycostlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - ping · ping-callback-receives-errorwarningWhenping() is called when connection is CLOSING or CLOSED and a callback is provided. The callback receives an error but no exception is thrown synchronously.Throws
No synchronous throw. If a callback is provided, it receives: Error: WebSocket is not open: readyState X (CLOSING|CLOSED)Required handlingIf using ping() with a callback for error detection, the callback MUST check for error. Heartbeat implementations that check for errors in the ping callback should handle this gracefully rather than logging/crashing. The more reliable pattern is to check readyState === WebSocket.OPEN before pinging.costlowin prodsilent failureusers seedegraded performancevisibilitysilentSources[11] - handleUpgrade · handleupgrade-missing-wsclienterror-handlerwarningWhenhandleUpgrade() receives an invalid upgrade request (missing or invalid Sec-WebSocket-Key, Sec-WebSocket-Version, Upgrade header, or non-GET method). Server has no 'wsClientError' event listener attached.Throws
No throw. Without a wsClientError listener, ws calls abortHandshake() directly and closes the socket with an HTTP error response (400/405). The upgrade request fails silently from the server application's perspective — no event is emitted, no log is produced.Required handlingWhen using noServer mode, ALWAYS attach a wsClientError handler: wss.on('wsClientError', (error, socket, request) => { socket.write('HTTP/1.1 400 Bad Request\r\n\r\n'); socket.destroy(); }); The wsClientError listener is responsible for closing the socket. Without it, malformed upgrade requests are silently dropped with no observability — making debugging impossible in production. Critical: The socket MUST be explicitly closed in the wsClientError handler. ws does NOT close it automatically when the event is emitted.costlowin prodsilent failureusers seedegraded performancevisibilitysilent - handleUpgrade · handleupgrade-called-in-non-noserver-modeerrorWhenhandleUpgrade() is called manually when the server was NOT created with noServer: true — i.e., ws is already handling upgrades automatically via its internal server event listeners.Throws
The socket may be handled twice, leading to the completeUpgrade() call throwing Error: "websocket already set on socket" or connection state corruption. Behavior is undefined and may cause hard-to-debug connection failures.Required handlinghandleUpgrade() MUST only be called when using noServer: true mode. When passing a server option or a port option, ws handles upgrades automatically. Do NOT call handleUpgrade() in that case. noServer mode example (Next.js API route pattern): const wss = new WebSocketServer({ noServer: true }); server.on('upgrade', (req, socket, head) => { wss.handleUpgrade(req, socket, head, (ws) => { wss.emit('connection', ws, req); }); });costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - WebSocketServer.close · server-close-already-stoppedwarningWhenWebSocketServer.close() is called on a server that is already closed (either never started, or close() already called and completed).Throws
No synchronous throw. The callback (if provided) receives: Error: The server is not runningRequired handlingAlways check server state before calling close(), or handle the error in the callback: wss.close((err) => { if (err) { // Server was already stopped — safe to ignore in cleanup code if (err.message !== 'The server is not running') throw err; } }); This error is benign in shutdown/cleanup flows where close() may be called defensively. Do NOT ignore all errors in the callback — only the 'not running' case.costlowin prodsilent failureusers seedegraded performancevisibilitysilent - WebSocketServer.close · server-close-existing-connections-not-terminatedwarningWhenWebSocketServer.close() is called while clients are still connected. Existing WebSocket connections continue to work — only new connections are rejected. The server's close event fires before all connections drain.Throws
Does NOT throw. Existing connections continue unaffected.Required handlingIf graceful shutdown is required, terminate all active connections before or after calling close(): wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.close(1001, 'Server shutting down'); } }); wss.close(() => { process.exit(0); }); Without explicit termination, long-lived connections keep the server process alive indefinitely after close() returns.costlowin prodsilent failureusers seedegraded performancevisibilitysilentSources[12]
Sources
Every postcondition cites at least one of these. Numbered to match the footnotes above.
- [1]github.com/websockets/wshttps://github.com/websockets/ws/issues/246
- [2]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/doc/ws.md#event-error
- [3]tools.ietf.org/html/rfc6455https://tools.ietf.org/html/rfc6455#section-7.4.1
- [4]websocket.org/reference/close-codeshttps://websocket.org/reference/close-codes/
- [5]github.com/websockets/wshttps://github.com/websockets/ws/issues/1170
- [6]skylinecodes.substack.com/p/backpressure-in-websocket-streamshttps://skylinecodes.substack.com/p/backpressure-in-websocket-streams
- [7]github.com/websockets/wshttps://github.com/websockets/ws/issues/1543
- [8]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/doc/ws.md#event-error-1
- [9]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/doc/ws.md#event-wsclienterror
- [10]owasp.org/www-community/vulnerabilitieshttps://owasp.org/www-community/vulnerabilities/Cross-Site_WebSocket_Hijacking
- [11]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/lib/websocket.js
- [12]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/doc/ws.md
- [13]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/lib/websocket-server.js
- [14]github.com/websockets/wshttps://github.com/websockets/ws#multiple-servers-sharing-a-single-https-server
Need a different package?
Request a profile