Skip to content
Latchkey

ws "WebSocket is not open: readyState 3 (CLOSED)" in CI

The Node ws library throws "WebSocket is not open: readyState 3 (CLOSED)" when you call .send() after the socket has closed. readyState 3 is CLOSED; the write had nowhere to go.

What this error means

A test throws from ws.send(...) with this message. It flakes because the test sends before the open event, or after the server already closed the connection.

ws
Error: WebSocket is not open: readyState 3 (CLOSED)
    at WebSocket.send (node_modules/ws/lib/websocket.js:...)

Diagnose it: is the test deterministic?

Before debugging an assertion, establish whether the test fails consistently. A test that passes alone and fails in the suite is sharing state; one that fails intermittently is racing something. Neither is fixed in the assertion.

Terminal
# in isolation
<runner> path/to/one.test

# order dependence
<runner> --shuffle   # or the runner equivalent

# raciness
for i in $(seq 1 20); do <runner> path/to/one.test || break; done

Common causes

send() called before open or after close

The test writes to the socket while it is still connecting (readyState 0) and it later closes, or writes after the peer closed it (readyState 3).

The server closed the connection early

A rejected handshake or an unhandled error on the server closes the socket, and the client's next send hits a CLOSED state.

How to fix it

Send only after the open event

  1. Wait for open before the first send.
  2. Guard sends with a readyState === WebSocket.OPEN check.
  3. Handle the close and error events so failures are visible, not silent.
test.mjs
ws.on('open', () => ws.send('hello'));
ws.on('error', (e) => done(e));

Guard every send with readyState

Never call send blindly; check the socket is OPEN first.

test.mjs
if (ws.readyState === ws.OPEN) ws.send(payload);

How to prevent it

  • Await the open event before sending in tests.
  • Attach close/error handlers so an early close surfaces clearly.
  • Guard sends with an OPEN readyState check.

Frequently asked questions

What causes ws "WebSocket is not open: readyState 3 (CLOSED)" in CI?
There are 2 common causes: send() called before open or after close and the server closed the connection early. The test writes to the socket while it is still connecting (readyState 0) and it later closes, or writes after the peer closed it (readyState 3).
How do I fix ws "WebSocket is not open: readyState 3 (CLOSED)" in CI?
There are 2 fixes depending on which cause you have: send only after the open event and guard every send with readystate. Work through them in order, since the first is the most common.
What does ws "WebSocket is not open: readyState 3 (CLOSED)" in CI actually mean?
A test throws from ws.send(...) with this message.
How do I stop ws "WebSocket is not open: readyState 3 (CLOSED)" in CI happening again?
Await the open event before sending in tests. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card