moeru-ai/airi · error · Error

Tool invocation " " is missing or ambiguous.

Error message

Tool invocation "${invocationId}" is missing or ambiguous.

What it means

findInvocation locates a tool invocation within a message's generationTranscript rounds by callId, optionally narrowed by invocationId. When invocationId is given, exactly one matching candidate must exist; zero or multiple matches make the reference ambiguous or nonexistent, so it throws rather than guessing which invocation to rerun.

Solutions

  1. Refresh the invocation id from the current message transcript before rerunning (re-read message.generationTranscript).
  2. If the callId legitimately appears multiple times, omit invocationId to get the ambiguity error and then select a specific invocation id.
  3. Verify the invocationId belongs to the same message whose transcript you pass in.
  4. Fix data producing duplicate invocation ids if the transcript was imported/generated incorrectly.

Example fix

// before
findInvocation(message, { callId, invocationId: 'stale-id' })
// after
const invocation = findInvocation(freshMessage, { callId, invocationId: currentInvocationId })
if (!invocation) return // call no longer present
Defensive patterns

Strategy: try-catch

Validate before calling

const transcript = message.generationTranscript?.rounds.flatMap(r => r.toolInvocations.filter(c => c.callId === callId)) ?? []
if (transcript.length === 0) return null

Try / catch

try {
  const found = findInvocation(message, { callId, invocationId })
} catch (e) {
  if (e.message.includes('missing or ambiguous')) {
    refreshTranscript(message.id)
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Calling findInvocation (via target/invocation selectors) with an invocationId that no longer exists in the transcript, matches multiple stored invocations, or when the message has no generationTranscript/rounds at all.

Common situations: Rerunning a tool call after the session was edited/regenerated so ids changed; passing an invocation id copied from another message; transcripts containing duplicate invocation ids after a buggy import; referencing a call from a cleared history.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of moeru-ai/airi@438a067dde (2026-09-17). Data as JSON: /api/errors/21aaf1ad33749a8b. Report an issue: GitHub.

Appendix: source

Thrown at packages/stage-ui/src/stores/tool-call-rerun.ts:50

}

interface ExecuteToolCallRerunOptions<TToolset extends string = string> {
  messages: ChatHistoryItem[]
  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

View on GitHub (pinned to 438a067dde)