n8n-io/n8n · error · Error
Static tool name collision — the following tool names resolv
Error message
Static tool name collision — the following tool names resolve to duplicates: ${staticCollisions.join(', ')} What it means
Thrown by Agent.build() when findDuplicateToolNames(finalStaticTools) returns a non-empty list, meaning two or more statically-registered tools share the same name. Tool names must be unique within an agent because the LLM uses them to select which tool to call, and the runtime dispatches by name — duplicates create ambiguity.
Source
Thrown at packages/@n8n/agents/src/sdk/agent.ts:1001
finalDeferredTools.some((t) => t.suspendSchema);
const mcpNeedsCheckpoint = this.mcpClients.some((c) => c.declaresApproval());
if ((staticNeedsCheckpoint || mcpNeedsCheckpoint) && !this.checkpointStore) {
throw new Error(
`Agent "${this.name}" has tools requiring approval or suspend/resume but no checkpoint storage. ` +
"Add .checkpoint('memory') for in-process storage, " +
'or pass a persistent store (e.g. LibSQLStore, PgStore).',
);
}
// Resolve tools from all MCP clients.
const mcpToolLists = await Promise.all(this.mcpClients.map(async (c) => await c.listTools()));
const mcpTools = ensureUniqueMcpToolNames(mcpToolLists.flat());
const mcpConnectionFailures = this.getMcpConnectionFailures();
// Detect collisions between direct, deferred, and MCP tools.
const staticCollisions = findDuplicateToolNames(finalStaticTools);
if (staticCollisions.length > 0) {
throw new Error(
`Static tool name collision — the following tool names resolve to duplicates: ${staticCollisions.join(', ')}`,
);
}
const staticNames = new Set(finalStaticTools.map((t) => t.name));
const reservedDeferredToolNames = new Set([
SEARCH_TOOLS_TOOL_NAME,
LOAD_TOOL_TOOL_NAME,
...RUNTIME_SKILL_TOOL_NAMES,
]);
const deferredNames = new Set<string>();
const deferredCollisions: string[] = [];
for (const tool of finalDeferredTools) {
if (
staticNames.has(tool.name) ||
reservedDeferredToolNames.has(tool.name) ||
deferredNames.has(tool.name)
) {View on GitHub (pinned to 5ac6606e81)
Solutions
- Inspect the error message which lists the colliding names, then rename the duplicate tools.
- Use a namespace prefix when combining tools from multiple sources (e.g. 'github_search' vs 'web_search').
- Dedupe the tool array before passing it to .tools() using a Set or Map keyed by name.
Example fix
// before
agent.tools([
{ name: 'search', ... },
{ name: 'search', ... }, // collision
]);
// after
agent.tools([
{ name: 'web_search', ... },
{ name: 'db_search', ... },
]); Defensive patterns
Strategy: validation
Validate before calling
function findDuplicates(tools: { name: string }[]): string[] {
const seen = new Set<string>();
const dupes = new Set<string>();
for (const t of tools) {
if (seen.has(t.name)) dupes.add(t.name);
seen.add(t.name);
}
return [...dupes];
}
const dupes = findDuplicates(myTools);
if (dupes.length > 0) {
// rename before passing to .tools()
} Prevention
- Dedupe tool arrays by name before passing them to .tools().
- Namespace tools from different sources with a prefix (e.g. 'github_search').
- Run a uniqueness check in your agent factory or builder helper.
When it happens
Trigger: Calling agent.tools([toolA, toolB]) where toolA.name === toolB.name. Or calling .tools() twice with tools that have overlapping names. Also possible when importing tool arrays from different modules that happen to share a name.
Common situations: Two different MCP servers or tool factories producing tools with generic names like 'search' or 'get'. Copying a tool and forgetting to rename it. Registering both a custom tool and a library-provided tool with the same name. Dynamic tool generation that does not enforce uniqueness.
Related errors
- MCP tool name collision — the following tool names resolve t
- Deferred tool name collision — the following tool names reso
- Tool name "${reservedTool.name}" is reserved for runtime ski
- The selected tools are not supported by "${currentAgentType}
- Deferred tool name "${tool.name}" is reserved
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/7cb71590d04822c3.
Report an issue: GitHub.