{"record":{"id":"eea9d93a83cb6775","repo":"siyuan-note/siyuan","slug":"websocket-is-not-open-state-d","errorCode":null,"errorMessage":"WebSocket is not open (state: %d)","messagePattern":"WebSocket is not open \\(state: (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/plugin/api_client.go","lineNumber":426,"sourceCode":"\n\t\t\t\tvar messageData []byte\n\t\t\t\tvar opcode gws.Opcode\n\t\t\t\tif data := sendCall.Argument(0); isJsValueNotNull(data) {\n\t\t\t\t\tif arrayBuffer, ok := data.Export().(goja.ArrayBuffer); ok {\n\t\t\t\t\t\topcode = gws.OpcodeBinary\n\t\t\t\t\t\tb := arrayBuffer.Bytes()\n\t\t\t\t\t\tmessageData = make([]byte, len(b))\n\t\t\t\t\t\tcopy(messageData, b) // ArrayBuffer.Bytes() points into JS engine memory; copy before async send\n\t\t\t\t\t} else {\n\t\t\t\t\t\topcode = gws.OpcodeText\n\t\t\t\t\t\tmessageData = []byte(data.String())\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tsendRunErr := p.worker.Run(func(rt *goja.Runtime) (_ any, err error) {\n\t\t\t\t\tstate := WebSocketState(readyState.Load())\n\t\t\t\t\tif state == WebSocketReadyStateClosing || state == WebSocketReadyStateClosed {\n\t\t\t\t\t\terr = fmt.Errorf(\"WebSocket is not open (state: %d)\", state)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tc := gwsConn.Load()\n\t\t\t\t\tif c == nil {\n\t\t\t\t\t\terr = fmt.Errorf(\"WebSocket not yet connected\")\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tupdateBufferedAmount(rt, len(messageData))\n\t\t\t\t\tc.WriteAsync(opcode, messageData, func(writeErr error) {\n\t\t\t\t\t\tp.worker.Run(func(rt *goja.Runtime) (_ any, err error) {\n\t\t\t\t\t\t\tif writeErr == nil {\n\t\t\t\t\t\t\t\tupdateBufferedAmount(rt, -len(messageData))\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\terr = writeErr\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/api_client.go#L408-L444","documentation":"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.","triggerScenarios":"Calling send() after close() was called, or from a timer/handler that fires after the peer closed the connection.","commonSituations":"Heartbeat/keep-alive senders that keep firing after close; message sends racing with an onclose event.","solutions":["Guard send() with ws.readyState === 1 (OPEN).","Stop senders/heartbeat timers in onclose before they can fire again."],"exampleFix":"// before\nws.close();\nawait ws.send('hi'); // rejects: WebSocket is not open (state: 2)\n// after\nif (ws.readyState === 1) {\n  await ws.send('hi');\n}","handlingStrategy":"validation","validationCode":"async function safeSend(ws, data) {\n  if (ws.readyState !== 1) return; // drop silently when not OPEN\n  return ws.send(data);\n}","typeGuard":"const isOpen = (ws) => ws.readyState === 1;","tryCatchPattern":"try { await ws.send(data); }\ncatch (e) { if (/WebSocket is not open/.test(String(e))) stopSenders(); }","preventionTips":["Always check readyState === 1 before send.","Cancel heartbeat timers in onclose."],"tags":["client-socket","websocket","send","lifecycle"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}