Profiles·Public

socket.io

semver^4.0.0postconditions15functions10last verified2026-06-18coverage score100%

Postconditions — what we check

  • emit · emit-no-error-handling
    warning
    Whenemit fails due to disconnected client or serialization error
    ThrowsError may be thrown or silently fail
    Required handlingRegister 'error' event listener or wrap emit in try-catch when using acknowledgement callbacks
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • on · on-handler-no-error-handling
    error
    Whenasync operation within event handler throws
    ThrowsUnhandled promise rejection crashes server
    Required handlingWrap all async operations inside event handlers with try-catch blocks
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • emitWithAck · emit-with-ack-no-timeout-hangs
    error
    Whensocket.emitWithAck() called without a prior .timeout(ms) call, and the client never sends an acknowledgement (disconnects, ignores the event, or crashes).
    ThrowsThe Promise NEVER rejects — it hangs indefinitely until the server process exits or the socket is garbage collected. There is no built-in deadline.
    Required handlingCaller MUST chain .timeout(ms) before .emitWithAck() in production code: const response = await socket.timeout(5000).emitWithAck("my-event", payload); Without timeout(), a missed acknowledgement leaks the Promise and its registered ack callback permanently. In high-traffic servers, this causes memory exhaustion.
    costhighin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[3][4]
  • emitWithAck · emit-with-ack-timeout-unhandled
    error
    Whensocket.timeout(ms).emitWithAck() called but the returned Promise is not wrapped in try-catch, and the client fails to acknowledge within the timeout window.
    ThrowsError with message "operation has timed out" — thrown as an unhandled promise rejection, confirmed from socket.js source: new Error("operation has timed out").
    Required handlingCaller MUST wrap in try-catch when using timeout: try { const response = await socket.timeout(5000).emitWithAck("confirm", data); } catch (err) { // err.message === "operation has timed out" // Client did not acknowledge — handle timeout (retry, fallback, log) } Unhandled rejection crashes the server process in Node.js versions < 15, or exits the process with unhandledRejection in Node.js >= 15 depending on config.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3][4]
  • socket.join · socket-join-adapter-error
    warning
    Whensocket.join() called with a distributed adapter (e.g., @socket.io/redis-adapter, @socket.io/mongo-adapter) and the adapter's backend is unreachable or returns an error.
    ThrowsPromise rejects with the adapter's error — for Redis adapter this is an IORedisError or generic Error from ioredis when Redis is unavailable. With in-memory adapter, join() is synchronous and never throws.
    Required handlingWhen using distributed adapters, MUST wrap await socket.join() in try-catch: try { await socket.join(roomName); } catch (err) { // Adapter (Redis/Mongo) failed — socket not in room // Log error and decide: disconnect socket, retry, or proceed without room } A failed join means the socket is NOT in the room — any subsequent room broadcasts will miss this client, causing silent message delivery failures.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[5][6]
  • fetchSockets · fetch-sockets-adapter-error
    error
    Whenio.fetchSockets() called in a clustered deployment with a distributed adapter (Redis, Postgres, etc.) and the adapter's backend is unavailable or times out.
    ThrowsPromise rejects with the adapter-specific error. For Redis adapter: IORedisError or connection-related Error from ioredis. For Postgres adapter: database Error. With in-memory adapter, fetchSockets() does not fail.
    Required handlingCaller MUST wrap in try-catch in clustered deployments: try { const sockets = await io.fetchSockets(); } catch (err) { // Adapter failed — socket list is unavailable // Return partial data or 503 to API caller } Unhandled rejection in an HTTP handler or cron job crashes the handler. Common use case: admin endpoint listing connected users — if unhandled, entire admin API returns 500.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[7][6]
  • fetchSockets · fetch-sockets-dynamic-namespace
    error
    Whenio.fetchSockets() called on a dynamic namespace (created via io.of(regex) pattern) which has no adapter assigned.
    ThrowsError("No adapter for this namespace, are you trying to get the list of clients of a dynamic namespace?") — thrown synchronously from broadcast-operator.js.
    Required handlingDo not call fetchSockets() on dynamic parent namespaces. Call it on specific namespace instances or on the default namespace (io). If unsure which namespace to query, use the specific namespace reference: const nsp = io.of("/my-namespace"); const sockets = await nsp.fetchSockets(); // correct
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8]
  • Server.close · server-close-http-server-error
    warning
    Whenio.close() called and the underlying HTTP server was not running or has already been closed (e.g., calling close() twice).
    ThrowsThe Promise resolves regardless (never rejects). However, the optional callback receives an Error: the HTTP server error is passed to fn(err) where err is "Server is not running." or similar Node.js http.Server close error. If close() is called without a callback, HTTP server errors are silently swallowed.
    Required handlingAlways pass a callback to detect HTTP server close failures: await io.close((err) => { if (err) { console.error('HTTP server close error:', err.message); } }); Or use the Promise return value for sequencing, but note that errors from the HTTP layer are only surfaced via the callback, not via Promise rejection.
    costlowin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[9]
  • Server.use · server-use-middleware-missing-next
    error
    WhenMiddleware registered via io.use() has a code path that does not call next() (e.g., early return without calling next, thrown error not caught before next).
    ThrowsConnection hangs — no error is thrown to the caller. The socket connection stays in connecting state until it times out (connectTimeout, default 45000ms). During this time the socket consumes resources but cannot communicate.
    Required handlingMUST call next() in every code path within middleware: io.use((socket, next) => { if (!isAuthenticated(socket)) { return next(new Error("unauthorized")); // reject } next(); // allow }); Async middleware MUST handle all error paths: io.use(async (socket, next) => { try { await authenticate(socket.handshake.auth.token); next(); } catch (err) { next(new Error("authentication failed")); // MUST catch and next(err) } });
    costhighin proddelayed failureusers seeservice unavailablevisibilitysilent
    Sources[10]
  • Server.use · server-use-middleware-async-unhandled-error
    error
    WhenAsync middleware registered via io.use() throws or rejects without catching the error and calling next(err). The middleware uses async/await but has no try-catch.
    ThrowsUnhandled promise rejection — Node.js emits an unhandledRejection event. In Node.js >= 15 with default settings, process exits. The connection hangs until timeout because next() was never called.
    Required handlingAsync middleware MUST use try-catch: io.use(async (socket, next) => { try { const user = await verifyToken(socket.handshake.auth.token); socket.data.user = user; next(); } catch (err) { next(new Error("authentication failed")); } });
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[10]
  • serverSideEmitWithAck · server-side-emit-with-ack-timeout
    error
    Whenio.serverSideEmitWithAck() called in a multi-server deployment and one or more servers fail to acknowledge within the adapter's default timeout window.
    ThrowsPromise rejects with an Error. The error object has an additional .responses property containing the acknowledgements received before the timeout: err.responses = [...responses from servers that did respond...] The default timeout is set by the adapter (varies by adapter implementation).
    Required handlingCaller MUST wrap in try-catch: try { const responses = await io.serverSideEmitWithAck("invalidate-cache", key); // all servers acknowledged } catch (err) { // err.responses contains partial responses // Some servers may have processed the event even though we got an error // Log err.responses for reconciliation } Partial responses (err.responses) are important — some servers may have already processed the event. This is especially critical for cache invalidation or configuration changes where partial application causes inconsistency.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[11]
  • socket.use · socket-use-middleware-error-no-listener
    error
    Whensocket.use() middleware calls next(err) with an Error, but no 'error' event listener is registered on the socket instance. This is distinct from Server.use() — per-socket middleware errors emit on the socket, not as a process-level unhandled rejection.
    Throwssocket._onerror(err) calls this.emitReserved("error", err). If no 'error' listener exists on the socket, EventEmitter throws the error synchronously, crashing the connection handler. In Node.js, an EventEmitter 'error' event with no listener throws the error — confirmed from Node.js EventEmitter docs. The process may crash or the unhandledRejection fires.
    Required handlingMUST register an 'error' listener on every socket that uses socket.use() middleware: io.on("connection", (socket) => { socket.use(([event, ...args], next) => { if (isUnauthorized(event)) { return next(new Error("unauthorized event")); } next(); }); // REQUIRED: handle middleware errors socket.on("error", (err) => { console.error("Socket middleware error:", err.message); socket.disconnect(); // or other remediation }); }); Without the 'error' listener, any next(err) call crashes the socket handler and potentially the server process.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[10]
  • socket.use · socket-use-middleware-async-unhandled
    error
    WhenAsync middleware registered via socket.use() throws or rejects without a try-catch, causing next() to never be called and the error to propagate as an unhandled rejection.
    ThrowsUnhandled promise rejection — Node.js emits an 'unhandledRejection' event. In Node.js >= 15, process exits. The packet that triggered the middleware is silently dropped, causing the client to miss the expected response.
    Required handlingAsync socket.use() middleware MUST use try-catch: socket.use(async ([event, ...args], next) => { try { await validateEvent(event, args); next(); } catch (err) { next(new Error("event validation failed")); } }); socket.on("error", (err) => { socket.disconnect(); });
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[10]
  • BroadcastOperator.emitWithAck · broadcast-emit-with-ack-timeout-unhandled
    error
    Whenio.timeout(ms).emitWithAck() called but the returned Promise is not wrapped in try-catch, and one or more targeted clients fail to acknowledge within the timeout window.
    ThrowsError with message "operation has timed out" — confirmed from broadcast-operator.js source. The error object has an additional .responses property containing all acknowledgements received before the timeout expired: err.responses = [...responses from clients that did ack in time...] This Promise rejects — unhandled rejection crashes the server in Node.js >= 15.
    Required handlingMUST wrap in try-catch. Also check .responses for partial acknowledgements: try { const responses = await io.timeout(5000).emitWithAck("confirm", payload); // all targeted clients acknowledged } catch (err) { // err.message === "operation has timed out" // err.responses contains partial responses from clients that did ack console.error(`${err.responses.length} clients acked before timeout`); // decide: retry, proceed with partial, or fail the operation } Partial responses (err.responses) are critical for idempotency — some clients may have already processed the event before the timeout. Do NOT retry blindly.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[12][4]
  • BroadcastOperator.emitWithAck · broadcast-emit-with-ack-no-timeout
    error
    Whenio.to(room).emitWithAck() called WITHOUT first chaining .timeout(ms), and one or more targeted clients never send an acknowledgement (disconnect, ignore, or crash).
    ThrowsThe Promise NEVER resolves or rejects — it hangs indefinitely. There is no built-in deadline for BroadcastOperator.emitWithAck() when .timeout() is not chained. Each disconnected or unresponsive client holds the Promise open permanently. In high-traffic servers handling many rooms, this causes Promise accumulation, memory leak, and eventual OOM crash.
    Required handlingALWAYS chain .timeout(ms) before .emitWithAck() in broadcast contexts: // WRONG - hangs forever if any client disconnects: const responses = await io.to("room-101").emitWithAck("sync", data); // RIGHT - rejects with "operation has timed out" after 5s: const responses = await io.to("room-101").timeout(5000).emitWithAck("sync", data);
    costhighin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[12]

Sources

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

  1. [1]socket.io/docs/v4https://socket.io/docs/v4/emitting-events/
  2. [2]socket.io/docs/v4https://socket.io/docs/v4/listening-to-events/
  3. [3]socket.io/docs/v4https://socket.io/docs/v4/server-api/#socketemitwithacheck
  4. [4]socket.io/docs/v4https://socket.io/docs/v4/emitting-events/#acknowledgements
  5. [5]socket.io/docs/v4https://socket.io/docs/v4/rooms/
  6. [6]socket.io/docs/v4https://socket.io/docs/v4/redis-adapter/
  7. [7]socket.io/docs/v4https://socket.io/docs/v4/server-api/#serverfetchsockets
  8. [8]socket.io/docs/v4https://socket.io/docs/v4/namespaces/#dynamic-namespaces
  9. [9]socket.io/docs/v4https://socket.io/docs/v4/server-api/#serverclose
  10. [10]socket.io/docs/v4https://socket.io/docs/v4/middlewares/
  11. [11]socket.io/docs/v4https://socket.io/docs/v4/server-api/#serverserversideemitwithack
  12. [12]socket.io/docs/v4https://socket.io/docs/v4/server-api/#broadcastoperatoremitwithacheck
Need a different package?
Request a profile