Profiles·Public

ws

semver>=8.17.1postconditions17functions7last verified2026-04-16coverage score88%

Postconditions — what we check

  • WebSocket · missing-error-handler
    error
    WhenWebSocket instance created without error event handler
    ThrowsEmits 'error' event that crashes process if not handled
    Required 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 unavailablevisibilityvisible
    Sources[1]
  • WebSocket · connection-error
    error
    WhenConnection fails (network error, DNS failure, timeout, handshake failure)
    ThrowsEmits 'error' event with Error object
    Required 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 unavailablevisibilityvisible
    Sources[2]
  • WebSocket · protocol-violation
    error
    WhenWebSocket protocol violation (invalid frames, reserved bits set)
    ThrowsEmits 'error' event and closes connection with code 1002
    Required 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 unavailablevisibilityvisible
    Sources[3]
  • WebSocket · missing-close-handler
    warning
    WhenConnection closes but no close event handler attached
    ThrowsEmits 'close' event with code and reason
    Required 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 unavailablevisibilityvisible
    Sources[4]
  • send · send-before-open
    error
    Whensend() called before connection is open (readyState !== OPEN)
    ThrowsError: 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 unavailablevisibilityvisible
    Sources[5]
  • send · backpressure-exceeded
    warning
    WhenSend buffer full (bufferedAmount exceeds threshold)
    ThrowsDoes NOT throw, but causes memory exhaustion and OOM crashes
    Required 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 unavailablevisibilityvisible
    Sources[6]
  • send · message-too-large
    error
    WhenMessage size exceeds maxPayload
    ThrowsRangeError: Invalid WebSocket frame: payload length > maxPayload
    Required 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 unavailablevisibilityvisible
    Sources[7]
  • WebSocketServer · server-error
    error
    WhenServer error (port in use, permission denied, etc.)
    ThrowsEmits 'error' event on server
    Required 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 unavailablevisibilityvisible
    Sources[8]
  • WebSocketServer · client-error-before-connection
    warning
    WhenClient error before connection established (handshake failure)
    ThrowsEmits 'wsClientError' event on server
    Required 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 unavailablevisibilityvisible
    Sources[9]
  • WebSocketServer · no-origin-validation
    warning
    WhenServer accepts connections without origin validation
    ThrowsDoes NOT throw, but allows cross-origin attacks
    Required 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 unavailablevisibilityvisible
    Sources[10]
  • close · close-emits-event
    warning
    WhenConnection closes (gracefully or abnormally)
    ThrowsEmits 'close' event with code and reason
    Required 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 unavailablevisibilityvisible
    Sources[4]
  • ping · ping-before-open
    error
    Whenping() is called while the WebSocket is still in CONNECTING state (readyState === WebSocket.CONNECTING, i.e., before the 'open' event fires).
    ThrowsError: 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 immediately
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[11][12]
  • ping · ping-callback-receives-error
    warning
    Whenping() is called when connection is CLOSING or CLOSED and a callback is provided. The callback receives an error but no exception is thrown synchronously.
    ThrowsNo 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 performancevisibilitysilent
    Sources[11]
  • handleUpgrade · handleupgrade-missing-wsclienterror-handler
    warning
    WhenhandleUpgrade() 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.
    ThrowsNo 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
    Sources[12][13]
  • handleUpgrade · handleupgrade-called-in-non-noserver-mode
    error
    WhenhandleUpgrade() 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.
    ThrowsThe 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
    Sources[12][14]
  • WebSocketServer.close · server-close-already-stopped
    warning
    WhenWebSocketServer.close() is called on a server that is already closed (either never started, or close() already called and completed).
    ThrowsNo synchronous throw. The callback (if provided) receives: Error: The server is not running
    Required 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
    Sources[13][12]
  • WebSocketServer.close · server-close-existing-connections-not-terminated
    warning
    WhenWebSocketServer.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.
    ThrowsDoes 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 performancevisibilitysilent
    Sources[12]

Sources

Every postcondition cites at least one of these. Numbered to match the footnotes above.

  1. [1]github.com/websockets/wshttps://github.com/websockets/ws/issues/246
  2. [2]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/doc/ws.md#event-error
  3. [3]tools.ietf.org/html/rfc6455https://tools.ietf.org/html/rfc6455#section-7.4.1
  4. [4]websocket.org/reference/close-codeshttps://websocket.org/reference/close-codes/
  5. [5]github.com/websockets/wshttps://github.com/websockets/ws/issues/1170
  6. [6]skylinecodes.substack.com/p/backpressure-in-websocket-streamshttps://skylinecodes.substack.com/p/backpressure-in-websocket-streams
  7. [7]github.com/websockets/wshttps://github.com/websockets/ws/issues/1543
  8. [8]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/doc/ws.md#event-error-1
  9. [9]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/doc/ws.md#event-wsclienterror
  10. [10]owasp.org/www-community/vulnerabilitieshttps://owasp.org/www-community/vulnerabilities/Cross-Site_WebSocket_Hijacking
  11. [11]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/lib/websocket.js
  12. [12]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/doc/ws.md
  13. [13]github.com/websockets/wshttps://github.com/websockets/ws/blob/master/lib/websocket-server.js
  14. [14]github.com/websockets/wshttps://github.com/websockets/ws#multiple-servers-sharing-a-single-https-server
Need a different package?
Request a profile