JuliusBrussee/caveman · error · Error
cave_untrusted_tool_definition:${declared.name}
Error message
cave_untrusted_tool_definition:${declared.name} What it means
Thrown by validateAgentGraph (packages/agent/src/definition-graph.ts:46): a tool definition lacks the TOOLS_IMPLEMENTATION_SOURCE marker (the Symbol.for('@caveman-ai/agent:tool-implementation-source') string property). Only tools created by the framework's own tool()/subagent() factories carry that symbol, so its absence means the object was hand-forged rather than produced by the trusted builder API.
Source
Thrown at packages/agent/src/definition-graph.ts:46
}
const memo = visited[sandboxRequired ? 1 : 0]!;
if (memo.has(definition)) return;
active.add(definition);
const childSandboxRequired = sandboxRequired ||
definition.sandbox === "required";
const names = new Set<string>();
for (const declared of definition.tools) {
if (!declared || declared.kind !== "tool" ||
typeof declared.name !== "string") {
throw new Error("cave_tool_definition_invalid");
}
if (names.has(declared.name)) throw new Error("cave_duplicate_tool_name");
names.add(declared.name);
if (declared.name.startsWith("cave_")) {
throw new Error(`cave_reserved_tool_name:${declared.name}`);
}
if (typeof Reflect.get(declared, TOOL_IMPLEMENTATION_SOURCE) !== "string") {
throw new Error(`cave_untrusted_tool_definition:${declared.name}`);
}
if (declared.runtime?.kind !== "subagent") continue;
const child = declared.runtime.definition as AgentDefinition;
visit(child, depth + 1, childSandboxRequired);
}
active.delete(definition);
memo.add(definition);
};
visit(root, 0, false);
}
/**
* True when any agent in the graph opts into host mode.
*
* Lock eligibility is a property of the whole graph, not of its root: a host
* subagent runs its tool closures in the host process just as a host root does,
* so its evidence shows no containment either. Follows the same subagent edgesView on GitHub (pinned to 27d5a3981a)
Solutions
- Create every tool with the exported tool() (and subagents with subagent()) from @caveman-ai/agent instead of object literals.
- If definitions must cross a process/serialization boundary, re-create tools via the factory on the receiving side rather than deserializing tool objects.
- Check for duplicate installations of @caveman-ai/agent (Symbol.for is cross-realm, but a factory that stops setting the symbol indicates a version mismatch) and align versions.
Example fix
// before
const myTool = { kind: "tool", name: "search", /* ... */ } as ToolDefinition;
// after
import { tool } from "@caveman-ai/agent";
const myTool = tool({ name: "search", /* ... */ }); Defensive patterns
Strategy: type-guard
Validate before calling
import { tool } from "@caveman-ai/agent";
// Every tool in the array must come from the factory — verify by construction:
function assertFactoryTools(tools: unknown[]): void {
for (const t of tools) {
if (typeof Reflect.get(Object(t), Symbol.for("@caveman-ai/agent:tool-implementation-source")) !== "string") {
throw new Error(`tool '${(t as { name?: string }).name}' was not created via tool()`);
}
}
} Type guard
const TOOL_SRC = Symbol.for("@caveman-ai/agent:tool-implementation-source");
function isFactoryTool(value: unknown): value is ToolDefinition {
return typeof value === "object" && value !== null &&
typeof Reflect.get(value, TOOL_SRC) === "string";
} Prevention
- Never hand-write ToolDefinition object literals; always use tool().
- Never clone tools with spread/JSON — re-create them through the factory.
- Keep a single version of @caveman-ai/agent installed across the workspace.
When it happens
Trigger: Passing a plain object literal (or an object spread/cloned from a real tool) in AgentDefinition.tools and then running validateAgentGraph, compile, or the dev/build CLI. Structured cloning, JSON round-trips, or {...realTool} spreads drop non-enumerable/symbol properties and also trigger it.
Common situations: Hand-assembling a ToolDefinition to bypass the factory; serializing/deserializing tool definitions; deep-cloning a tool with a spread or a cloner that drops symbol-keyed properties; constructing tools with a different (incompatible) version of the package than the one validating the graph.
Related errors
- cave_tool_definition_invalid
- cave_reserved_tool_name:${declared.name}
- cave_memory_tenant_invalid
- cave_memory_agent_invalid
- cannot safely launch Windows command shim: ${executable}
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/647d67daabb20a44.
Report an issue: GitHub.