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

  1. Listen for the 'call-handler-error' event and key on callName to find the failing handler fast
  2. Validate the call arguments (e.g. with Valibot) at the handler entry and return a structured error to the model instead of throwing
  3. 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

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


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/b398bc48bd9a9a21. Report an issue: GitHub.