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
- Confirm the endpoint and scheme: wss:// for https origins, ws:// only for localhost dev.
- Keep idle connections alive or lower proxy/LB idle timeouts so the socket is not aborted mid-session.
- For auth failures, ensure the token in urlRef is fresh — the client intentionally stops reconnecting after close code 4001 until token rotation reopens it.
- 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
- Match ws/wss scheme to the page origin.
- Configure proxies/load balancers with WebSocket upgrade support and adequate idle timeouts.
- Refresh auth tokens before expiry so the 4001 path never triggers.
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
- [chat-ws] socket error:
- [chat-sync] pullMessages failed for
- [chat-sync] DELETE /api/v1/chats failed for
- [chat-sync] listChats failed; skipping reconcile this round:
- [chat-ws] dropped malformed newMessages payload:
AI-assisted analysis of moeru-ai/airi@b6d0809ecb (2026-08-18).
Data as JSON: /api/errors/6a3d98927ebfec78.
Report an issue: GitHub.