moeru-ai/airi · error · Error

Tamagotchi extension tool not found: ${key}

Error message

Tamagotchi extension tool not found: ${key}

What it means

Thrown by TamagotchiToolRegistry.invoke() when no tool record exists for the composite key `${ownerExtensionId}:${toolId}`. The registry is an in-memory Map keyed by that composite, so the invoke cannot route to an execute handler.

Source

Thrown at packages/plugin-sdk-tamagotchi/src/tools/registry.ts:233

      items.push({
        ownerExtensionId: record.ownerExtensionId,
        name: record.tool.id,
        description: record.tool.description,
        parameters: structuredClone(record.tool.parameters),
      })
    }

    return {
      prompts: await this.listToolsetPrompts(),
      tools: items,
    }
  }

  async invoke(ownerExtensionId: string, toolId: string, input: unknown) {
    const key = `${ownerExtensionId}:${toolId}`
    const record = this.tools.get(key)
    if (!record) {
      throw new Error(`Tamagotchi extension tool not found: ${key}`)
    }

    return await record.execute(input)
  }
}

View on GitHub (pinned to 27111382b4)

Solutions

  1. Verify the ownerExtensionId and toolId match exactly what was passed to registry.register().
  2. Call registry.listAvailableDescriptors() or check registry before invoke to confirm the tool exists and is available.
  3. Ensure invoke is not called after unregisterOwnerSession/clear/teardown has removed the tool.
  4. Coordinate xsai tool dispatch to skip tools that were withdrawn between listing and invocation.

Example fix

// before
await registry.invoke(extId, 'search', input) // throws if not registered under extId:search

// after — guard before invoke
const descriptors = await registry.listAvailableDescriptors()
const exists = descriptors.some(d => d.id === 'search')
if (!exists) throw new Error(`Tool 'search' is not available`)
await registry.invoke(extId, 'search', input)
Defensive patterns

Strategy: validation

Validate before calling

const descriptors = await registry.listAvailableDescriptors()
const exists = descriptors.some(d => d.id === toolId)
if (!exists) {
  throw new Error(`Tool '${toolId}' is not registered or available for extension '${ownerExtensionId}'`)
}
return registry.invoke(ownerExtensionId, toolId, input)

Type guard

async function isToolRegistered(registry: TamagotchiToolRegistry, ownerExtensionId: string, toolId: string): Promise<boolean> {
  const descriptors = await registry.listAvailableDescriptors()
  return descriptors.some(d => d.id === toolId)
}

Try / catch

try {
  return await registry.invoke(ownerExtensionId, toolId, input)
} catch (error) {
  if (error instanceof Error && /Tamagotchi extension tool not found/.test(error.message)) {
    // tool was never registered or was withdrawn; return a not-found response
    return { error: 'tool-not-found', toolId, ownerExtensionId }
  }
  throw error
}

Prevention

When it happens

Trigger: Calling registry.invoke(ownerExtensionId, toolId, input) where the tool was never registered, was unregistered via unregister/unregisterOwnerSession/unregisterOwnerScope/clear, or where the ownerExtensionId or toolId does not exactly match the values used at registration time.

Common situations: Invoker passes the wrong extension id (e.g. module id instead of extension id), a tool was unregistered during session teardown but an in-flight xsai call still references it, or a tool id typo/mismatch between registration and invocation. Also occurs when a tool's availability gate filtered it out but the caller invokes directly bypassing listAvailableDescriptors.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/bdec4875279d3fb6. Report an issue: GitHub.