ZhuLinsen/daily_stock_analysis · error · KeyError

Tool '{name}' not found in registry. Available: {self.list_n

Error message

Tool '{name}' not found in registry. Available: {self.list_names()}

What it means

KeyError raised by ToolRegistry.execute when resolve(name) returns None — i.e. no tool is registered under that exact name in this registry instance. The message lists all registered names to make the mismatch obvious. This fires before any handler runs, so it is purely a name-resolution failure, not a tool execution failure.

Source

Thrown at src/agent/tools/registry.py:290

                    "code": "stock_scope_parameter_missing",
                    "message": "Tool declares stock scope but has no stock_code parameter.",
                })
        return issues

    # ----- Execution -----

    def execute(self, name: str, **kwargs) -> Any:
        """Execute a registered tool by name.

        Returns the result as a JSON-serializable value.
        Raises ``KeyError`` if tool not found.
        Raises the handler's exception on execution failure.

        Tool names must match the registry exactly.
        """
        tool_def = self.resolve(name)
        if tool_def is None:
            raise KeyError(f"Tool '{name}' not found in registry. Available: {self.list_names()}")

        return tool_def.handler(**kwargs)


# ============================================================
# @tool decorator
# ============================================================

# Global default registry (singleton pattern)
_default_registry: Optional[ToolRegistry] = None


def get_default_registry() -> ToolRegistry:
    """Get or create the global default ToolRegistry."""
    global _default_registry
    if _default_registry is None:
        _default_registry = ToolRegistry()
    return _default_registry

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Check the names in the error's 'Available:' list and call execute with one of them exactly.
  2. If the tool genuinely should exist, verify it was registered (via @tool decorator or registry.register) on the SAME registry instance you are executing against.
  3. For LLM-driven calls, constrain the model's tool schema to registry.list_names() and retry or reject unknown names instead of executing blindly.

Example fix

# before
result = registry.execute("fetch_quote", symbol="AAPL")  # KeyError

# after
result = registry.execute("get_quote", symbol="AAPL")  # name from registry.list_names()
Defensive patterns

Strategy: type-guard

Validate before calling

names = set(registry.list_names())
if tool_name not in names:
    raise ValueError(f"unknown tool {tool_name!r}; known: {sorted(names)}")

Type guard

def is_registered_tool(registry, name: str) -> bool:
    return registry.resolve(name) is not None

Try / catch

try:
    result = registry.execute(name, **kwargs)
except KeyError as e:
    # message lists available names; fall back to a corrective action for LLM callers
    return {"error": "unknown_tool", "available": registry.list_names()}

Prevention

When it happens

Trigger: registry.execute('fetch_quote', ...) when only e.g. 'get_quote' is registered; calling a tool on a fresh/default registry that was never populated; case or underscore mismatches ('websearch' vs 'web_search'); calling after a registry reset where _default_registry was rebuilt empty.

Common situations: An LLM hallucinating a tool name not in the advertised schema; typos in dynamic dispatch code; tools registered on a custom registry instance while the caller uses the global default (or vice versa); tool renamed in a newer version but the caller still uses the old name.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/f7ed4fcd35000970. Report an issue: GitHub.