moeru-ai/airi · warning
Channel gateway stream error:
Error message
Channel gateway stream error:
What it means
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.
Source
Thrown at packages/stage-ui/src/stores/mods/api/channel-gateway.ts:92
channels.set(channel.name, channel)
if (!channel.in)
return
const reader = channel.in.getReader()
readers.set(channel.name, reader)
const pump = async () => {
try {
while (true) {
const result = await reader.read()
if (result.done)
break
dispatch(result.value, { origin: channel.name })
}
}
catch (error) {
console.warn('Channel gateway stream error:', channel.name, error)
}
}
void pump()
}
function unregister(name: string) {
channels.delete(name)
const reader = readers.get(name)
if (reader) {
reader.cancel().catch(() => undefined)
readers.delete(name)
}
}
function route(rule: GatewayRoute) {
routes.push(rule)
}View on GitHub (pinned to 677329427f)
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
Defensive patterns
Strategy: try-catch
Validate before calling
if (readers.has(channel.name)) return // pump already running for this channel
Try / catch
const pump = async () => {
try {
while (true) {
const result = await reader.read()
if (result.done) break
dispatch(result.value, { origin: channel.name })
}
}
catch (error) {
// reader.cancel() from unregister() races read(); AbortError here is an expected exit
console.warn('Channel gateway stream error:', channel.name, error)
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Network blip closing the shared WebSocket; dev-mode HMR tearing down transports; concurrent register/unregister of the same channel.
Related errors
- No WebSocket constructor is available. Pass `wsConstructor`
- WebSocket transport is not implemented for node runtime yet.
- WebSocket transport is not implemented for web runtime yet.
- Invalid WebSocket event format.
- Client is not connected, current status: ${this.status}
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/f8af138463c81749.
Report an issue: GitHub.