siyuan-note/siyuan · warning
WebSocket is not open (state: %d)
Error message
WebSocket is not open (state: %d)
What it means
Returned by socket.send() when readyState is CLOSING (2) or CLOSED (3). send() refuses to queue a frame on a connection that is shutting down or gone, and rejects the send promise with the numeric state.
Source
Thrown at kernel/plugin/api_client.go:426
var messageData []byte
var opcode gws.Opcode
if data := sendCall.Argument(0); isJsValueNotNull(data) {
if arrayBuffer, ok := data.Export().(goja.ArrayBuffer); ok {
opcode = gws.OpcodeBinary
b := arrayBuffer.Bytes()
messageData = make([]byte, len(b))
copy(messageData, b) // ArrayBuffer.Bytes() points into JS engine memory; copy before async send
} else {
opcode = gws.OpcodeText
messageData = []byte(data.String())
}
}
sendRunErr := p.worker.Run(func(rt *goja.Runtime) (_ any, err error) {
state := WebSocketState(readyState.Load())
if state == WebSocketReadyStateClosing || state == WebSocketReadyStateClosed {
err = fmt.Errorf("WebSocket is not open (state: %d)", state)
return
}
c := gwsConn.Load()
if c == nil {
err = fmt.Errorf("WebSocket not yet connected")
return
}
updateBufferedAmount(rt, len(messageData))
c.WriteAsync(opcode, messageData, func(writeErr error) {
p.worker.Run(func(rt *goja.Runtime) (_ any, err error) {
if writeErr == nil {
updateBufferedAmount(rt, -len(messageData))
} else {
err = writeErr
}
returnView on GitHub (pinned to 251596fc0d)
Solutions
- Guard send() with ws.readyState === 1 (OPEN).
- Stop senders/heartbeat timers in onclose before they can fire again.
Example fix
// before
ws.close();
await ws.send('hi'); // rejects: WebSocket is not open (state: 2)
// after
if (ws.readyState === 1) {
await ws.send('hi');
} Defensive patterns
Strategy: validation
Validate before calling
async function safeSend(ws, data) {
if (ws.readyState !== 1) return; // drop silently when not OPEN
return ws.send(data);
} Type guard
const isOpen = (ws) => ws.readyState === 1;
Try / catch
try { await ws.send(data); }
catch (e) { if (/WebSocket is not open/.test(String(e))) stopSenders(); } Prevention
- Always check readyState === 1 before send.
- Cancel heartbeat timers in onclose.
When it happens
Trigger: Calling send() after close() was called, or from a timer/handler that fires after the peer closed the connection.
Common situations: Heartbeat/keep-alive senders that keep firing after close; message sends racing with an onclose event.
Related errors
- WebSocket is closing
- WebSocket is closed
- WebSocket not yet connected
- WebSocket is not open (state: %d)
- Recorder has been disposed
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/eea9d93a83cb6775.
Report an issue: GitHub.