moeru-ai/airi · error · Error
Unknown action: ${step.tool}
Error message
Unknown action: ${step.tool} What it means
Thrown by ActionRegistry.performAction when no registered Action has a name equal to step.tool. The registry is seeded from actionsList (llm-actions.ts) and matching is exact (===). This fires after the mineflayer-instance guard, so the registry is ready but the requested tool name is not in its catalog.
Source
Thrown at integrations/minecraft/src/cognitive/action/action-registry.ts:42
/**
* Get all available actions
*/
public getAvailableActions(): Action[] {
return [...this.actions]
}
/**
* Perform an action by name
*/
public async performAction(step: { description?: string, tool: string, params: any }): Promise<unknown> {
if (!this.mineflayer) {
throw new Error('Mineflayer instance not set in ActionRegistry')
}
const action = this.actions.find(a => a.name === step.tool)
if (!action) {
throw new Error(`Unknown action: ${step.tool}`)
}
const actionFn = action.perform(this.mineflayer)
const { schema } = action
const parsedParams = schema.parse(step.params || {})
// Extract parameter values in the order defined by the schema
const paramValues = Object.keys((schema as any).shape || {}).map(key => parsedParams[key])
const result = await actionFn(...paramValues)
return result ?? `Action ${step.tool} completed`
}
/**
* Register a new action
*/
public registerAction(action: Action): void {
this.actions.push(action)View on GitHub (pinned to 27111382b4)
Solutions
- Compare step.tool against registry.getAvailableActions().map(a => a.name) — names are case-sensitive.
- Refresh the tool catalog / system prompt sent to the LLM whenever actionsList changes.
- Register the action via registry.registerAction if it is a legitimate new capability.
- If hallucinated, constrain the model with a stricter function-calling schema or retry with the valid list.
Example fix
// before
registry.performAction({ tool: 'CollectBlocks', params: { type: 'oak_log', num: 1 } })
// after
registry.performAction({ tool: 'collectBlocks', params: { type: 'oak_log', num: 1 } }) Defensive patterns
Strategy: validation
Validate before calling
const validNames = new Set(registry.getAvailableActions().map(a => a.name))
if (!validNames.has(step.tool))
throw new Error(`Unknown action: ${step.tool}. Valid: ${[...validNames].join(', ')}`) Type guard
function isRegisteredAction(registry, name) {
return registry.getAvailableActions().some(a => a.name === name)
} Try / catch
try {
await registry.performAction(step)
} catch (e) {
if (e.message.startsWith('Unknown action:'))
// refresh the LLM tool catalog and retry with a valid name
throw e
} Prevention
- Regenerate the tool catalog sent to the LLM whenever actionsList changes.
- Match action names exactly (case-sensitive).
- Register new actions before the planner can reference them.
When it happens
Trigger: An LLM emits a tool name with a typo or that was hallucinated; a custom action was registered under a different name than the planner uses; the action was removed/renamed but the planner prompt or model cache still references the old name.
Common situations: Model hallucination of a tool not in the function list; case mismatch (e.g. 'CollectBlocks' vs 'collectBlocks'); stale tool catalog sent to the LLM after an actionsList edit; calling performAction directly with an unregistered name in a test.
Related errors
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/12699563d31d74f2.
Report an issue: GitHub.