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
- Re-derive the rerun action from the current messages array and confirm the target id still resolves to an assistant message.
- Reload chat history and retry the rerun from the fresh state.
- If history was transformed (compaction, summarization), drop the stale rerun action rather than retrying.
- 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
- Render rerun actions from the live history store, not cached message objects.
- Re-validate target role and toolCallId immediately before executing.
- Key rerun payloads by messageId + toolCallId and invalidate them on history mutation.
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
- Assistant message does not contain tool call "${payload.tool
- MiMo voice clone requires a base64 audio sample in data URI
- MiMo voice design requires a style prompt in the user messag
- streaming models upstream missing models[]
- Invalid speech request body
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/020ca85c31e5e5af.
Report an issue: GitHub.