moeru-ai/airi · error · Error
Select this tool before running it again.
Error message
Select this tool before running it again.
What it means
rerunToolCall re-executes a previously stored tool call. Some tools are gated by requiresToolSelection: the caller must pass a tools list containing the tool being rerun. If the payload's tools array does not include payload.toolName, the store refuses to run it — the user must explicitly select/enable the tool before rerunning.
Solutions
- Include the tool in payload.tools when calling rerunToolCall (the UI should pass the currently enabled tool definitions).
- Have the user enable/select the tool in the chat UI before retrying.
- Check requiresToolSelection(toolName) beforehand and prompt for selection instead of calling.
- Verify the tool name string matches the registered tool's name exactly.
Example fix
// before
await rerunToolCall({ sessionId, toolName: 'web_search' })
// after
await rerunToolCall({ sessionId, toolName: 'web_search', tools: enabledTools }) Defensive patterns
Strategy: validation
Validate before calling
const canRerun = !requiresToolSelection(payload.toolName) || payload.tools?.some(t => t.name === payload.toolName) if (!canRerun) enableToolFirst(payload.toolName)
Try / catch
try {
await rerunToolCall(payload)
} catch (e) {
if (e.message === 'Select this tool before running it again.')
ui.promptToolSelection(payload.toolName)
else throw e
} Prevention
- Always pass the full currently-enabled tool list in the rerun payload.
- Disable retry buttons for tools not in the active tool set.
- Match tool names against registered tool definitions, not free-form strings.
When it happens
Trigger: Calling rerunToolCall({ toolName, ... }) where requiresToolSelection(toolName) is true and payload.tools is undefined or does not contain an entry with name === toolName.
Common situations: A UI 'retry this tool call' button that reruns without sending the currently enabled tool list; a tool disabled in settings after its original run; programmatically rerunning a gated tool without reconstructing the tools payload.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Failed to load the target chat session
- Failed to load the target chat session
- No active chat provider or model configured
- Retry target has no retriable source message
- Retry target has no retriable user message
AI-assisted analysis of moeru-ai/airi@438a067dde (2026-09-17).
Data as JSON: /api/errors/7f03ffda025b481c.
Report an issue: GitHub.
Appendix: source
Thrown at packages/stage-ui/src/stores/chat.ts:500
try {
return await executeSend({
sessionId: payload.sessionId,
text,
replyToMessageId: sourceMessage?.replyToMessageId,
tools: payload.tools ?? sourceMessage?.tools?.filter(tool => !requiresToolSelection(tool.name)),
})
}
catch (error) {
appendSendError(payload.sessionId, error)
throw error
}
}
/** Runs one stored tool call again and replaces its stored result. */
async function rerunToolCall(payload: ChatToolCallRerunPayload): Promise<void> {
if (requiresToolSelection(payload.toolName) && !payload.tools?.some(tool => tool.name === payload.toolName))
throw new Error('Select this tool before running it again.')
if (!await chatSession.loadSession(payload.sessionId))
throw new Error('Failed to load the target chat session')
const nextMessages = await executeToolCallRerun({
messages: chatSession.getSessionMessages(payload.sessionId),
payload,
resolveTools: () => resolveLlmTools({
customTools: llmToolsStore.getToolsByNames(payload.toolName),
}),
})
chatSession.setSessionMessages(payload.sessionId, nextMessages)
}
/** Clears one session and stops runtime work that still belongs to it. */
function cleanup(sessionId: string) {
chatSession.cleanupMessages(sessionId)
chatContext.resetContexts()View on GitHub (pinned to 438a067dde)