siyuan-note/siyuan · error

WebSocket not yet connected

Error message

WebSocket not yet connected

What it means

The ready state is OPEN but the underlying gws connection handle (gwsConn) has not been populated yet — a race between the JS-visible OPEN transition and the Go-side connection pointer being stored. send() finds c == nil and rejects with 'WebSocket not yet connected'.

Solutions

  1. Retry the send after a short delay or on the next tick; the window is transient.
  2. Serialize sends: perform the first send inside a setTimeout/onopen-followup rather than synchronously after open resolves.
  3. If it persists, the connection dropped between state update and send — recreate the socket.

Example fix

// before
await socket.open();
socket.send('hello');
// after
await socket.open();
setTimeout(() => {
  if (socket.readyState === 1) socket.send('hello');
}, 0);
Defensive patterns

Strategy: retry

Validate before calling

if (socket.readyState === 1) { /* still can race; add a tick delay before first send */ }

Try / catch

try {
  socket.send(first);
} catch (e) {
  if (String(e).includes('not yet connected')) {
    setTimeout(() => socket.readyState === 1 && socket.send(first), 50);
  }
}

Prevention

When it happens

Trigger: Calling send() immediately after the open promise resolves, before the internal connection reference is published; concurrent send during the connection handshake completion window.

Common situations: Code that sends a subscription/register message in the first microtask after open resolves; fast local connections where JS state flips before the atomic pointer store is observed.

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


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/250f2640bd65d01b. Report an issue: GitHub.

Appendix: source

Thrown at kernel/plugin/api_client.go:432

						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
							}
							return
						}, func(rt *goja.Runtime, result any, err error) {
							if lo.IsNil(err) {
								if resolveErr := sendResolve(result); resolveErr != nil {
									logging.LogErrorf("[plugin:%s] siyuan.client.socket.send resolve: %v", p.Name, resolveErr)
								}
							} else {

View on GitHub (pinned to 9f775e8a12)