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

  1. Rename one of the colliding tools so every name in the array is unique.
  2. If you meant to override, replace the original entry instead of appending.
  3. 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

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


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/9745727dae33f5e8. Report an issue: GitHub.