siyuan-note/siyuan · warning
WebSocket is closed
Error message
WebSocket is closed
What it means
Returned by socket.open() when readyState is CLOSED (3). The connection has finished closing; open() will not transition it back to open.
Source
Thrown at kernel/plugin/api_client.go:338
})
}
ws_open := rt.ToValue(func(openCall goja.FunctionCall, rt *goja.Runtime) goja.Value {
openPromise, openResolve, openReject := rt.NewPromise()
openRunErr := p.worker.Run(func(rt *goja.Runtime) (_ any, err error) {
state := WebSocketState(readyState.Load())
switch state {
case WebSocketReadyStateOpen:
if resolveErr := openResolve(nil); resolveErr != nil {
logging.LogErrorf("[plugin:%s] siyuan.client.socket.open resolve: %v", p.Name, resolveErr)
}
return
case WebSocketReadyStateClosing:
err = fmt.Errorf("WebSocket is closing")
return
case WebSocketReadyStateClosed:
err = fmt.Errorf("WebSocket is closed")
return
}
addOpenPromise(openResolve, openReject)
openOnce.Do(func() {
go func() {
conn, _, dialErr := gws.NewClient(h, &gws.ClientOption{
Addr: wsURL,
RequestHeader: wsHeader,
})
if dialErr != nil {
p.worker.Run(func(rt *goja.Runtime) (_ any, _ error) {
setReadyState(rt, WebSocketReadyStateClosed)
event := rt.NewObject()
event.Set("type", rt.ToValue("error"))
event.Set("error", rt.NewGoError(dialErr))
invokeHook(rt, "onerror", event)View on GitHub (pinned to 251596fc0d)
Solutions
- Create a new connection via siyuan.client.socket(path) and open() that, rather than reopening the closed one.
- Check readyState === CLOSED and branch to recreate.
Example fix
// before
// ws is already closed
await ws.open(); // rejects: WebSocket is closed
// after
const ws = await siyuan.client.socket('/ws/foo');
await ws.open(); Defensive patterns
Strategy: validation
Validate before calling
if (ws.readyState === 3) {
ws = await siyuan.client.socket(path); // recreate
}
await ws.open(); Type guard
const isClosed = (ws) => ws.readyState === 3;
Try / catch
try { await ws.open(); }
catch (e) { if (/WebSocket is closed/.test(String(e))) { ws = await siyuan.client.socket(path); await ws.open(); } } Prevention
- Never reopen a closed socket; recreate it.
- Track readyState in your connection manager.
When it happens
Trigger: Calling open() on a socket whose close handshake completed, or after the dial failed and readyState was set to CLOSED.
Common situations: Reconnect logic that calls open() on the original socket object instead of creating a new one; or calling open() after a failed dial.
Related errors
- WebSocket is closing
- WebSocket is not open (state: %d)
- 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/cc2a9dcfed776e5a.
Report an issue: GitHub.