moeru-ai/airi · warning
[llm-streaming-control] handler failed
Error message
[llm-streaming-control] handler failed
What it means
The same llm-streaming-control controller runs registered handlers for parsed 'call' tokens (tool calls requested by the model). Each call handler is awaited inside its own try/catch; a rejection is emitted as a 'call-handler-error' event carrying callName plus the error, logged here, and the stream continues with a 'call-handler-end' for cleanup. The controller does not crash — the named tool call simply produced no result.
Source
Thrown at packages/pipelines-audio/src/llm-streaming-control/controller.ts:380
await handler(
parsed.payload,
signalContext,
)
emit(context, {
type: 'call-handler-end',
callName: parsed.name,
})
}
catch (error) {
emit(context, {
type: 'call-handler-error',
callName: parsed.name,
error,
})
console.warn(
'[llm-streaming-control] handler failed',
error,
)
}
}
return true
},
on(manifest, handler) {
return registerHandler(
{
handlers,
callManifests,
},
manifest,
handler,
)View on GitHub (pinned to 677329427f)
Solutions
- Listen for the 'call-handler-error' event and key on callName to find the failing handler fast
- Validate the call arguments (e.g. with Valibot) at the handler entry and return a structured error to the model instead of throwing
- Keep handlers side-effect-failure-tolerant: catch inside the handler for non-fatal failures and report them as tool results
Example fix
// before
controller.on(manifest, async (parsed) => {
const args = parsed.arguments as { query: string }
await runSearch(args.query) // throws if arguments have another shape
})
// after
controller.on(manifest, async (parsed) => {
const parsed_args = ArgsSchema.safeParse(parsed.arguments)
if (!parsed_args.success)
return { error: 'invalid arguments' }
return runSearch(parsed_args.data.query)
}) Defensive patterns
Strategy: try-catch
Type guard
const ArgsSchema = v.object({ query: v.string() })
function parseCallArgs(parsed: unknown) {
return ArgsSchema.safeParse((parsed as { arguments?: unknown })?.arguments)
} Try / catch
try {
return await runTool(parsed.arguments)
}
catch (error) {
// return the failure to the model instead of rejecting the handler
return { error: errorMessageFrom(error) }
} Prevention
- Validate model-produced tool arguments with a schema (Valibot) at handler entry
- Listen for 'call-handler-error' with callName to find the failing tool fast
- Return structured error results rather than throwing, so streams keep flowing
When it happens
Trigger: A tool-call handler registered via controller.on throwing for a specific call: unvalidated arguments from the model, missing context (no audio runtime, closed channel), or an unhandled promise inside the handler.
Common situations: The model emits tool arguments that don't match the handler's expected schema; a handler depending on state that is not yet initialized at first token; network or filesystem side effects failing.
Related errors
- [llm-streaming-control] signal handler failed
- [parseActEmotion] Failed to parse ACT payload JSON: "${paylo
- streaming models upstream ${res.status}: ${await res.text().
- streaming models upstream missing models[]
- streaming voices upstream ${res.status}: ${await res.text().
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/b398bc48bd9a9a21.
Report an issue: GitHub.