moeru-ai/airi · error · Error
Tool call " " is ambiguous. Select an invocation.
Error message
Tool call "${callId}" is ambiguous. Select an invocation. What it means
findInvocation resolves a tool call ID to a single concrete invocation. When a tool call ID appears more than once in the message's candidates (e.g. parallel or repeated invocations of the same tool), it cannot pick one, so it throws to force the caller to disambiguate via an invocationId. This is an internal invariant guard protecting rerun correctness.
Solutions
- Pass an explicit invocationId in the rerun payload so findInvocation can select the exact occurrence.
- Inspect the target assistant message's tool call candidates and pick the intended invocation before rerunning.
- If duplication is unexpected, check upstream message construction for duplicated tool call entries.
Example fix
// before
await rerunToolCall({ toolCallId: 'call_1', toolName: 'search' })
// after
await rerunToolCall({ toolCallId: 'call_1', toolName: 'search', invocationId: 'inv_2' }) Defensive patterns
Strategy: validation
Validate before calling
// Count invocations for the callId before rerunning
const matches = message.toolCalls.filter(c => c.id === toolCallId)
if (matches.length > 1 && !invocationId)
throw new Error(`Tool call "${toolCallId}" is ambiguous; pass an invocationId.`) Try / catch
try {
await rerunToolCall(payload)
} catch (e) {
if (e.message.includes('is ambiguous')) {
// surface a picker UI listing invocations for this callId
}
} Prevention
- Always include invocationId when a message may contain duplicate tool call IDs.
- Validate candidate counts in UI before enabling the rerun action.
- Avoid generating duplicate tool call IDs when constructing messages.
When it happens
Trigger: Calling findInvocation (via target/invocation helpers in tool-call-rerun.ts) with a callId that has no invocationId while the assistant message contains 2+ tool call candidates with that same call ID.
Common situations: Re-running a tool call from a message where the model issued the same tool call multiple times (parallel tool use, or duplicated call IDs after message merging/editing), and the rerun payload omitted the invocationId discriminator.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Assistant message does not contain tool call
- The selected invocation does not match the tool name.
- Tool call rerun target must be an assistant message.
- Tool invocation " " is missing or ambiguous.
- JSON.stringify(result)
AI-assisted analysis of moeru-ai/airi@438a067dde (2026-09-17).
Data as JSON: /api/errors/f1b81d1df0a4515a.
Report an issue: GitHub.
Appendix: source
Thrown at packages/stage-ui/src/stores/tool-call-rerun.ts:54
payload: ToolCallRerunPayload<TToolset>
resolveTools: () => Promise<Tool[]>
}
type ToolCallResultInput = Omit<ChatSlicesToolCallResult, 'type'>
type ToolExecuteOptions = NonNullable<Parameters<Tool['execute']>[1]>
function findInvocation(message: ChatAssistantMessage, callId: string, invocationId?: string) {
const candidates = message.generationTranscript?.rounds.flatMap((round, roundIndex) =>
round.toolInvocations.filter(call => call.callId === callId).map(call => ({ call, roundIndex })),
)
if (invocationId !== undefined) {
const matches = candidates?.filter(candidate => candidate.call.id === invocationId)
if (!candidates || matches?.length !== 1)
throw new Error(`Tool invocation "${invocationId}" is missing or ambiguous.`)
return { ...matches[0], occurrence: candidates.indexOf(matches[0]) }
}
if (candidates && candidates.length > 1)
throw new Error(`Tool call "${callId}" is ambiguous. Select an invocation.`)
return candidates?.[0] ? { ...candidates[0], occurrence: 0 } : undefined
}
/**
* Replaces one execution and its display results, preserving their order.
* Native continuation for this round and later rounds becomes stale after the edit.
* Repeated provider call ids require an AIRI invocation id.
*/
export function replaceToolCallResult(message: ChatAssistantMessage, result: ToolCallResultInput, invocationId?: string): ChatAssistantMessage {
const target = findInvocation(message, result.id, invocationId)
const occurrence = target?.occurrence ?? 0
const toolResult = { id: result.id, isError: result.isError, result: result.result }
const resultSlice: ChatSlicesToolCallResult = { type: 'tool-call-result', ...toolResult }
let generationTranscript = message.generationTranscript
if (generationTranscript && target) {
generationTranscript = {
...generationTranscript,
rounds: generationTranscript.rounds.map((round, index) => index < target.roundIndexView on GitHub (pinned to 438a067dde)