moeru-ai/airi · error · Error

Tool call rerun target must be an assistant message.

Error message

Tool call rerun target must be an assistant message.

What it means

executeToolCallRerun locates the target message via findTargetMessageIndex and requires role === 'assistant', because the rerun replaces a tool-call result inside the assistant message that issued the call. The throw fires when the index resolves to nothing (targetMessage undefined) or to a user/system message.

Source

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

    ],
  }
}

/**
 * Re-executes a stored tool call with supplied arguments and returns updated chat history.
 *
 * The resolver is injected so callers can choose the runtime-specific tool list
 * without coupling this helper to app-local stores, Electron IPC, or browser state.
 */
export async function executeToolCallRerun<TToolset extends string = string>(
  options: ExecuteToolCallRerunOptions<TToolset>,
): Promise<ChatHistoryItem[]> {
  const { messages, payload } = options
  const targetIndex = findTargetMessageIndex(messages, payload)
  const targetMessage = messages[targetIndex]

  if (targetMessage?.role !== 'assistant')
    throw new Error('Tool call rerun target must be an assistant message.')

  if (!hasMatchingToolCall(targetMessage, payload))
    throw new Error(`Assistant message does not contain tool call "${payload.toolCallId}" for "${payload.toolName}".`)

  const replaceTargetMessage = (result: ToolCallResultInput) => messages.map((item, itemIndex) => {
    if (itemIndex !== targetIndex)
      return item

    return replaceToolCallResult(targetMessage, result)
  })

  const tools = await options.resolveTools()
  const tool = tools.find(candidate => toolNameFrom(candidate) === payload.toolName)
  if (tool == null) {
    return replaceTargetMessage({
      id: payload.toolCallId,
      isError: true,
      result: `Tool "${payload.toolName}" is not available for rerun in this runtime.`,

View on GitHub (pinned to 677329427f)

Solutions

  1. Re-derive the rerun action from the current messages array and confirm the target id still resolves to an assistant message.
  2. Reload chat history and retry the rerun from the fresh state.
  3. If history was transformed (compaction, summarization), drop the stale rerun action rather than retrying.
  4. Log targetIndex and the payload id at the call site to find which caller sends stale payloads.

Example fix

// before
const targetMessage = messages[targetIndex]
if (targetMessage?.role !== 'assistant')
  throw new Error('Tool call rerun target must be an assistant message.')

// after
const targetMessage = messages[targetIndex]
if (targetIndex < 0 || !targetMessage)
  throw new Error(`Tool call rerun target message not found (id="${payload.messageId}")`)
if (targetMessage.role !== 'assistant')
  throw new Error(`Tool call rerun target must be an assistant message, got "${targetMessage.role}"`)
Defensive patterns

Strategy: type-guard

Validate before calling

const idx = findTargetMessageIndex(messages, payload)
if (idx < 0 || messages[idx]?.role !== 'assistant') {
  // disable or hide the rerun action in the UI
}

Type guard

function isAssistantMessageWithToolCalls(m: ChatHistoryItem | undefined): m is ChatHistoryItem & { role: 'assistant' } {
  return !!m && m.role === 'assistant'
}

Try / catch

try {
  history = await executeToolCallRerun({ messages, payload, resolveTools })
}
catch (e) {
  if (errorMessageFrom(e)?.includes('must be an assistant message'))
    await reloadHistory() // stale snapshot
  else
    throw e
}

Prevention

When it happens

Trigger: Rerun payload carries a message id that no longer exists in the messages array; history was reordered or compacted so the index resolves to a different-role message; payload built from a stale snapshot of the chat.

Common situations: User regenerates or edits history, then clicks a rerun action rendered from the old snapshot; background history compaction removed the assistant message; tests passing a fabricated message list.

Related errors


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