{"record":{"id":"020ca85c31e5e5af","repo":"moeru-ai/airi","slug":"tool-call-rerun-target-must-be-an-assistant-messag","errorCode":null,"errorMessage":"Tool call rerun target must be an assistant message.","messagePattern":"Tool call rerun target must be an assistant message\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/stores/tool-call-rerun.ts","lineNumber":85,"sourceCode":"    ],\n  }\n}\n\n/**\n * Re-executes a stored tool call with supplied arguments and returns updated chat history.\n *\n * The resolver is injected so callers can choose the runtime-specific tool list\n * without coupling this helper to app-local stores, Electron IPC, or browser state.\n */\nexport async function executeToolCallRerun<TToolset extends string = string>(\n  options: ExecuteToolCallRerunOptions<TToolset>,\n): Promise<ChatHistoryItem[]> {\n  const { messages, payload } = options\n  const targetIndex = findTargetMessageIndex(messages, payload)\n  const targetMessage = messages[targetIndex]\n\n  if (targetMessage?.role !== 'assistant')\n    throw new Error('Tool call rerun target must be an assistant message.')\n\n  if (!hasMatchingToolCall(targetMessage, payload))\n    throw new Error(`Assistant message does not contain tool call \"${payload.toolCallId}\" for \"${payload.toolName}\".`)\n\n  const replaceTargetMessage = (result: ToolCallResultInput) => messages.map((item, itemIndex) => {\n    if (itemIndex !== targetIndex)\n      return item\n\n    return replaceToolCallResult(targetMessage, result)\n  })\n\n  const tools = await options.resolveTools()\n  const tool = tools.find(candidate => toolNameFrom(candidate) === payload.toolName)\n  if (tool == null) {\n    return replaceTargetMessage({\n      id: payload.toolCallId,\n      isError: true,\n      result: `Tool \"${payload.toolName}\" is not available for rerun in this runtime.`,","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui/src/stores/tool-call-rerun.ts#L67-L103","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst targetMessage = messages[targetIndex]\nif (targetMessage?.role !== 'assistant')\n  throw new Error('Tool call rerun target must be an assistant message.')\n\n// after\nconst targetMessage = messages[targetIndex]\nif (targetIndex < 0 || !targetMessage)\n  throw new Error(`Tool call rerun target message not found (id=\"${payload.messageId}\")`)\nif (targetMessage.role !== 'assistant')\n  throw new Error(`Tool call rerun target must be an assistant message, got \"${targetMessage.role}\"`)","handlingStrategy":"type-guard","validationCode":"const idx = findTargetMessageIndex(messages, payload)\nif (idx < 0 || messages[idx]?.role !== 'assistant') {\n  // disable or hide the rerun action in the UI\n}","typeGuard":"function isAssistantMessageWithToolCalls(m: ChatHistoryItem | undefined): m is ChatHistoryItem & { role: 'assistant' } {\n  return !!m && m.role === 'assistant'\n}","tryCatchPattern":"try {\n  history = await executeToolCallRerun({ messages, payload, resolveTools })\n}\ncatch (e) {\n  if (errorMessageFrom(e)?.includes('must be an assistant message'))\n    await reloadHistory() // stale snapshot\n  else\n    throw e\n}","preventionTips":["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."],"tags":["tool-call","chat-history","validation","rerun"],"backgroundTag":"chat-history-validation-failed","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}