{"record":{"id":"f8af138463c81749","repo":"moeru-ai/airi","slug":"channel-gateway-stream-error","errorCode":null,"errorMessage":"Channel gateway stream error:","messagePattern":"Channel gateway stream error:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/stage-ui/src/stores/mods/api/channel-gateway.ts","lineNumber":92,"sourceCode":"    channels.set(channel.name, channel)\n\n    if (!channel.in)\n      return\n\n    const reader = channel.in.getReader()\n    readers.set(channel.name, reader)\n\n    const pump = async () => {\n      try {\n        while (true) {\n          const result = await reader.read()\n          if (result.done)\n            break\n          dispatch(result.value, { origin: channel.name })\n        }\n      }\n      catch (error) {\n        console.warn('Channel gateway stream error:', channel.name, error)\n      }\n    }\n\n    void pump()\n  }\n\n  function unregister(name: string) {\n    channels.delete(name)\n    const reader = readers.get(name)\n    if (reader) {\n      reader.cancel().catch(() => undefined)\n      readers.delete(name)\n    }\n  }\n\n  function route(rule: GatewayRoute) {\n    routes.push(rule)\n  }","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui/src/stores/mods/api/channel-gateway.ts#L74-L110","documentation":"The async pump that reads a channel's ReadableStream threw — the stream errored or reader.read() rejected — so event dispatch for that channel stops. The channel and its reader stay registered unless unregister() runs, and the pump exits after the warn. Events on that channel are silently not delivered until the stream is re-established.","triggerScenarios":"Underlying WebSocket closing with an error while the pump awaits reader.read(); the provider stream erroring (serialization or protocol mismatch); reader.cancel() from unregister() racing an in-flight read.","commonSituations":"Network blip closing the shared WebSocket; dev-mode HMR tearing down transports; concurrent register/unregister of the same channel.","solutions":["Re-register the channel after the transport reconnects so a fresh pump starts","Treat AbortError from reader.cancel() as an expected exit, not a stream failure","When pumps die across all channels, check the underlying WebSocket client status and reconnect logs","Sequence register/unregister per channel (the readers map is the source of truth) to avoid double pumps"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (readers.has(channel.name)) return // pump already running for this channel","typeGuard":null,"tryCatchPattern":"const pump = async () => {\n  try {\n    while (true) {\n      const result = await reader.read()\n      if (result.done) break\n      dispatch(result.value, { origin: channel.name })\n    }\n  }\n  catch (error) {\n    // reader.cancel() from unregister() races read(); AbortError here is an expected exit\n    console.warn('Channel gateway stream error:', channel.name, error)\n  }\n}","preventionTips":["Cancel readers via unregister() before tearing down the underlying socket","Ignore AbortError from cancelled readers to reduce noise","Re-register channels after reconnect instead of assuming pumps survive","Sequence register/unregister per channel using the readers map as the source of truth"],"tags":["channel-gateway","readable-stream","websocket","event-dispatch"],"backgroundTag":"stream-read-failed","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}