moeru-ai/airi · warning

[chat-ws] ws error event:

Error message

[chat-ws] ws error event:

What it means

VueUse useWebSocket's onError callback fired: the underlying browser WebSocket emitted an 'error' event. DOM WebSocket error events have almost no payload (no message/code), which is why the log prints the bare event. It precedes a close event; the code in onClose handles 4001 (unauthorized) by pausing reconnect until the token rotates. This log is transport-level noise unless the socket cannot reconnect at all.

Source

Thrown at packages/stage-ui/src/libs/chat-sync/ws-client.ts:361

      // retrying. When the server rejects auth, the only structured
      // signal we get is the close `code` (the close `reason` body is
      // also delivered but not used for routing here). 4001 is our
      // protocol signal for "this token will never succeed without
      // rotation"; calling `ws.close()` here sets
      // useWebSocket's internal `explicitlyClosed` flag so the next
      // onclose path skips the reconnect schedule. A token change below
      // closes the old context and starts a new connection that authenticates
      // with the new token after opening.
      if (restartForNewToken && enabled.value && tokenRef.value) {
        ws.open()
      }
      else if (ev.code === WS_CLOSE_UNAUTHORIZED) {
        console.warn('[chat-ws] server rejected auth (4001), pausing reconnect until token rotates')
        ws.close()
      }
    },
    onError(_rawWs, event) {
      console.warn('[chat-ws] ws error event:', event)
    },
  })

  // Translate VueUse's 3-state status into our 4-state machine and fan it
  // out to the orchestrator. The chat store creates this inside a Pinia
  // setup, which gives us a parent effect scope for `watch` to attach to.
  // NOTICE:
  // We intentionally do NOT stop this watcher in `disconnect()`; previous
  // behavior killed it permanently and any caller that did `disconnect()`
  // followed by `connect()` silently stopped receiving status events. The
  // watcher is idle while the socket is closed, so leaving it attached
  // costs nothing. Use `destroy()` for terminal cleanup.
  const stopStatusWatch = watch(
    [ws.status, enabled, authenticated],
    ([rawStatus, isEnabled, isAuthenticated]) => notifyStatus(mapStatus(rawStatus, isEnabled, isAuthenticated)),
    { immediate: true },
  )

View on GitHub (pinned to b6d0809ecb)

Solutions

  1. Confirm the endpoint and scheme: wss:// for https origins, ws:// only for localhost dev.
  2. Keep idle connections alive or lower proxy/LB idle timeouts so the socket is not aborted mid-session.
  3. For auth failures, ensure the token in urlRef is fresh — the client intentionally stops reconnecting after close code 4001 until token rotation reopens it.
  4. Treat recurring pairs of this log with close code 1006 as a transport/network issue to fix server-side or in proxy config.

Example fix

// before
onError(_rawWs, event) => {
  console.warn('[chat-ws] ws error event:', event)
}

// after — include readyState and URL context since the event itself is opaque
onError(_rawWs, event) {
  console.warn('[chat-ws] ws error event:', event?.type, 'readyState:', _rawWs?.readyState, 'url:', urlRef.value)
}
Defensive patterns

Strategy: retry

Try / catch

// onError is informational; act on close codes instead
onClose(_ws, ev) {
  if (ev.code === WS_CLOSE_UNAUTHORIZED) {
    // rotate token; watcher on urlRef reopens the socket
  }
}

Prevention

When it happens

Trigger: Connection attempt to the chat-sync endpoint fails (refused/DNS/TLS), or an established socket aborts (network drop, server crash, proxy timeout). The 4001 path is handled separately in onClose, so this handler mainly covers non-auth transport faults.

Common situations: Dev server restarted while the tab stayed open; laptop sleep/resume dropping the socket; flaky Wi-Fi; load balancer idle-timeout killing wss connections; wrong URL scheme (wss against a plain-http port).

Related errors


AI-assisted analysis of moeru-ai/airi@b6d0809ecb (2026-08-18). Data as JSON: /api/errors/6a3d98927ebfec78. Report an issue: GitHub.