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

  1. Compare step.tool against registry.getAvailableActions().map(a => a.name) — names are case-sensitive.
  2. Refresh the tool catalog / system prompt sent to the LLM whenever actionsList changes.
  3. Register the action via registry.registerAction if it is a legitimate new capability.
  4. 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

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.