JuliusBrussee/caveman · error · Error
caveman agent: duplicate tool name
Error message
caveman agent: duplicate tool name
What it means
Thrown by the agent() builder (packages/agent/src/index.ts:103): two or more tools in the agent's tools array share the same name. Tool names are the dispatch keys for the model, so duplicates within one agent are rejected at construction time.
Source
Thrown at packages/agent/src/index.ts:103
];
export function agent(options: {
id: string;
instructions: string | FileSource;
model: Auto | string | Model<Api>;
reasoning?: AgentDefinition["reasoning"];
tools?: ToolDefinition[];
contexts?: ContextDefinition[];
memory?: MemoryDefinition;
output?: OutputDefinition;
sandbox?: AgentDefinition["sandbox"];
}): AgentDefinition {
if (!/^[a-z0-9][a-z0-9_-]{0,95}$/.test(options.id)) {
throw new Error(`caveman agent: invalid agent id ${JSON.stringify(options.id)}`);
}
const tools = Object.freeze([...(options.tools ?? [])]);
if (new Set(tools.map((item) => item.name)).size !== tools.length) {
throw new Error("caveman agent: duplicate tool name");
}
const sandbox = options.sandbox ?? "required";
if (!SANDBOX_MODES.includes(sandbox)) {
throw new Error(`caveman agent: unknown sandbox mode ${JSON.stringify(sandbox)}`);
}
const reserved = tools.find((item) => item.name.startsWith("cave_"));
if (reserved) {
throw new Error(
`caveman agent: tool prefix cave_ is reserved by framework (${reserved.name})`,
);
}
const definition: AgentDefinition = {
kind: "agent",
id: options.id,
instructions: options.instructions,
model: options.model,
reasoning: options.reasoning ?? "low",
tools,View on GitHub (pinned to 27d5a3981a)
Solutions
- Rename one of the colliding tools so every name in the array is unique.
- If you meant to override, replace the original entry instead of appending.
- Deduplicate by name before calling agent() when tool lists are assembled dynamically.
Example fix
// before
agent({ tools: [searchTool, searchToolWithFilters /* both named "search" */] });
// after
agent({ tools: [tool({ ...searchTool, name: "search" }), tool({ ...searchToolWithFilters, name: "search_filtered" })] }); Defensive patterns
Strategy: validation
Validate before calling
function hasDuplicateToolNames(tools: { name: string }[]): boolean {
return new Set(tools.map((t) => t.name)).size !== tools.length;
}
if (hasDuplicateToolNames(tools)) {
const seen = new Set<string>();
const dup = tools.find((t) => seen.has(t.name) || !seen.add(t.name))!.name;
throw new Error(`duplicate tool name: ${dup}`);
} Prevention
- Deduplicate dynamically assembled tool lists by name before agent().
- Replace a tool to override it instead of appending a second copy.
- Assert uniqueness in a shared helper used by all tool-registry code.
When it happens
Trigger: Calling agent({ tools: [...] }) where two ToolDefinition objects have the same name property — e.g. including the same tool twice, or two differently-configured tools with an identical name.
Common situations: Spreading a base tool list and appending an override of the same tool; copy-pasting a tool definition and forgetting to rename; composing tools from multiple modules that both define 'search'.
Related errors
- caveman agent: tool prefix cave_ is reserved by framework ($
- cave_duplicate_tool_name
- cave_reserved_tool_name:${declared.name}
- caveman agent: invalid agent id ${JSON.stringify(options.id)
- project name must contain a letter or number
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/9745727dae33f5e8.
Report an issue: GitHub.