siyuan-note/siyuan · error
WebSocket is not open
Error message
WebSocket is not open (state: %d)
What it means
send() was invoked while the socket ready state is CLOSING (2) or CLOSED (3). Data can only be written on an OPEN (1) connection, so the binding rejects with the numeric state for diagnosis. This is a WebSocket lifecycle guard, not a transport failure.
Solutions
- Check socket.readyState === 1 before sending; buffer or drop messages otherwise.
- Recreate the socket (connect + open) after it reaches CLOSED, then resend.
- Route sends through onopen so messages only go out on a live connection.
Example fix
// before
socket.send(JSON.stringify(msg));
// after
if (socket.readyState === 1) {
socket.send(JSON.stringify(msg));
} else {
pendingQueue.push(msg); // flush on next onopen
} Defensive patterns
Strategy: type-guard
Validate before calling
function canSend(s) { return s.readyState === 1; }
if (!canSend(socket)) queueMessage(msg); Type guard
const isOpen = (s) => s.readyState === 1;
Try / catch
try {
socket.send(data);
} catch (e) {
if (String(e).includes('WebSocket is not open')) {
pendingQueue.push(data); // flush on next onopen
}
} Prevention
- Gate all sends on readyState === 1
- Buffer outgoing messages while the socket is not open
- Close sockets explicitly on teardown so handlers stop sending
When it happens
Trigger: Calling socket.send(message) after close() was called, after onclose fired, or concurrently with a close in progress; also on a socket that never finished opening.
Common situations: Queued send callbacks executing after teardown; UI event handlers sending on a socket closed by kernel restart; missing readyState checks in send wrappers.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- WebSocket is closed
- WebSocket is closing
- WebSocket is not open
- WebSocket not yet connected
- agent runtime turn is not finalized
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/eea9d93a83cb6775.
Report an issue: GitHub.
Appendix: 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 9f775e8a12)