ws "WebSocket is not open: readyState 3 (CLOSED)" in CI
By Daniel Zoghalchali·Latchkey
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
Wait for open before the first send.
Guard sends with a readyState === WebSocket.OPEN check.
Handle the close and error events so failures are visible, not silent.
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.