JuliusBrussee/caveman · error · ValueError
Native tool is not registered
Error message
Native tool is not registered
What it means
After validation, selected() looks up exactly one registered tool whose metadata.name matches call.tool_name; zero or multiple matches raise ValueError because the selection cannot be resolved unambiguously.
Solutions
- Only dispatch tool calls whose tool_name matches a registered tool's metadata.name
- Re-register/re-create the executor if the tool set changed
- Check membership first: any(t.metadata.name == call.tool_name for t in tools)
Example fix
// before
executor.selected(call) # call.tool_name='get_weather' not registered
// after
names = {t.metadata.name for t in executor.tools}
if call.tool_name in names:
executor.selected(call) Defensive patterns
Strategy: validation
Validate before calling
registered = {t.metadata.name for t in executor.tools}
if call.tool_name not in registered:
raise KeyError(f"tool {call.tool_name!r} is not registered") Type guard
def is_registered(executor, tool_name: str) -> bool:
return sum(t.metadata.name == tool_name for t in executor.tools) == 1 Try / catch
try:
tool = executor.selected(call)
except ValueError as e:
log.warning("dispatch skipped: %s", e)
return error_tool_output(call) Prevention
- Constrain the LLM's tool choices to registered names
- Re-register the executor after any tool renaming
- Log hallucinated tool names instead of dispatching them
When it happens
Trigger: Dispatching a tool_id/name that is not in the executor's tools list, or after tools were changed so the name no longer matches.
Common situations: LLM hallucinating a tool name not registered, tools renamed after registration, or reusing an executor across sessions with different tool sets.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Caveman delegates through public chat_with_tools methods
- Caveman recovery metadata is immutable
- Caveman recovery tool is immutable
- Expected a native ToolSelection
- Expected an existing native LlamaIndex LLM
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/2c3ce02e79842481.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/python/caveman_middleware/llama_index.py:184
and tuple(tool.metadata.name for tool in tools) == self.names
and self.runtime.owns_binding(self.binding, self.scope)
and self.runtime.owns_binding(self.async_binding, self.scope)
and self.tool.fn is self.sync and self.tool.async_fn is self.async_
and self.tool.metadata is self.metadata and not self.tool.partial_params and not self.tool.requires_context
and self.metadata.description == RECOVERY_DESCRIPTION and not self.metadata.return_direct
and self.metadata.get_parameters_dict() == RECOVERY_SCHEMA)
def invocation(self):
return _Invocation(self, self.scope, self.tools, self.successful, True)
def selected(self, call: ToolSelection):
if self.binding is not None and not self.registered(self.tools, self.invocation()):
raise ValueError("Native executor registration changed")
if not isinstance(call, ToolSelection) or not isinstance(call.tool_id, str) or not call.tool_id or not isinstance(call.tool_kwargs, dict):
raise TypeError("Expected a native ToolSelection")
matches = [tool for tool in self.tools if tool.metadata.name == call.tool_name]
if len(matches) != 1:
raise ValueError("Native tool is not registered")
self.successful.pop(call.tool_id, None)
return matches[0]
def completed(self, call, result):
if not isinstance(result, ToolOutput):
raise TypeError("Native FunctionTool returned an unexpected output")
if not result.is_error and call.tool_name != "caveman_retrieve" and len(self.successful) < 4096:
self.successful[call.tool_id] = call.tool_name
return result
@dataclass(frozen=True)
class CavemanLLMTools:
"""Native model and tools for an application-owned loop; no scheduler."""
model: LLM
tools: tuple[FunctionTool, ...]
_registration: Any
View on GitHub (pinned to 3ee70a1026)