{"record":{"id":"9605901f7b3041b1","repo":"moeru-ai/airi","slug":"chat-ws-socket-error","errorCode":null,"errorMessage":"[chat-ws] socket error:","messagePattern":"\\[chat-ws\\] socket error:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/stage-ui/src/libs/chat-sync/ws-client.ts","lineNumber":281,"sourceCode":"      if (!result.success) {\n        console.warn('[chat-ws] dropped malformed newMessages payload:', result.issues[0]?.message)\n        return\n      }\n      const payload = result.output\n      for (const handler of newMessagesHandlers) {\n        try {\n          handler(payload)\n        }\n        catch (err) {\n          // Same isolation principle as notifyStatus: one bad listener should\n          // not silently drop messages for the rest.\n          console.warn('[chat-ws] newMessages handler threw:', errorMessageFrom(err))\n        }\n      }\n    }))\n\n    contextDisposers.push(ctx.on(wsErrorEvent, (event) => {\n      console.warn('[chat-ws] socket error:', event.body)\n    }))\n  }\n\n  // The URL ref controls the user connection intent and token presence. The\n  // token itself is sent only after the socket opens through Eventa.\n  const ws = useWebSocket<string>(urlRef, {\n    immediate: false,\n    autoClose: true,\n    autoReconnect: {\n      retries: RECONNECT_RETRIES,\n      delay: retries => computeReconnectDelay(\n        Math.max(retries, authenticationFailures),\n        RECONNECT_BASE_MS,\n        RECONNECT_MAX_MS,\n      ),\n    },\n    onConnected(rawWs) {\n      activeSocket = rawWs","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/moeru-ai/airi/blob/b6d0809ecb6421d492c4ce38aa7a76146c83269d/packages/stage-ui/src/libs/chat-sync/ws-client.ts#L263-L299","documentation":"The chat-sync WebSocket client (VueUse useWebSocket wrapper) received a ws 'error' event and logs it via the wsErrorEvent listener. Browser WebSocket error events carry no message — the meaningful detail is the accompanying close code — so this log is the generic signal that the socket transport failed (DNS, TLS, refused connection, proxy failure, or server-side abort) independent of the app-level message handlers.","triggerScenarios":"The chat sync server URL in urlRef is unreachable (wrong host/port, service down), a TLS certificate is rejected, an intermediary proxy kills the connection, or the server closes with an abnormal close code after an error. useWebSocket then schedules reconnect with the exponential backoff defined by computeReconnectDelay.","commonSituations":"Local dev without the chat-sync backend running; reverse proxy (Caddy/nginx) misrouting the /ws route; corporate proxy stripping Upgrade headers; expired/self-signed cert in dev; server restart causing transient error+close while autoReconnect recovers.","solutions":["Verify the chat sync server is running and the WebSocket URL in urlRef is correct (scheme ws/wss, host, path).","Check the paired close event/code — 4001 (WS_CLOSE_UNAUTHORIZED) means token rotation is needed; 1006 means abnormal transport failure.","Fix proxy config to pass through Upgrade/Connection headers and allow wss on the route.","If TLS-related, renew/repair certificates; the client's autoReconnect with backoff will recover once the transport is healthy."],"exampleFix":"// before\ncontextDisposers.push(ctx.on(wsErrorEvent, (event) => {\n  console.warn('[chat-ws] socket error:', event.body)\n}))\n\n// after — correlate with close code for diagnosis\ncontextDisposers.push(ctx.on(wsErrorEvent, (event) => {\n  console.warn('[chat-ws] socket error:', event.body, 'status:', status.value)\n}))\nws.onError((_ws, ev) => console.warn('[chat-ws] raw error; watch onclose code for cause', ev))","handlingStrategy":"retry","validationCode":"function isChatSyncUrlReachable(url: string): boolean {\n  try {\n    const parsed = new URL(url)\n    return parsed.protocol === 'ws:' || parsed.protocol === 'wss:'\n  }\n  catch {\n    return false\n  }\n}\n// validate urlRef before connect(); reachability itself is only proven by open","typeGuard":null,"tryCatchPattern":"// ws error events carry no Error object; handle at the status layer\nwatch(status, (s) => {\n  if (s === 'CLOSED') {\n    // rely on autoReconnect; surface offline state in UI\n  }\n})","preventionTips":["Validate the WS URL shape before connecting.","Keep autoReconnect with backoff enabled (already configured via computeReconnectDelay).","Correlate error logs with close codes — 4001 needs token rotation, 1006 is transport."],"tags":["websocket","network","chat-sync","reconnect"],"backgroundTag":"websocket-connection-error","analyzedSha":"b6d0809ecb6421d492c4ce38aa7a76146c83269d","analyzedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}