{"record":{"id":"7cb71590d04822c3","repo":"n8n-io/n8n","slug":"static-tool-name-collision-the-following-tool-na","errorCode":null,"errorMessage":"Static tool name collision — the following tool names resolve to duplicates: ${staticCollisions.join(', ')}","messagePattern":"Static tool name collision — the following tool names resolve to duplicates: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/sdk/agent.ts","lineNumber":1001,"sourceCode":"\t\t\tfinalDeferredTools.some((t) => t.suspendSchema);\n\t\tconst mcpNeedsCheckpoint = this.mcpClients.some((c) => c.declaresApproval());\n\t\tif ((staticNeedsCheckpoint || mcpNeedsCheckpoint) && !this.checkpointStore) {\n\t\t\tthrow new Error(\n\t\t\t\t`Agent \"${this.name}\" has tools requiring approval or suspend/resume but no checkpoint storage. ` +\n\t\t\t\t\t\"Add .checkpoint('memory') for in-process storage, \" +\n\t\t\t\t\t'or pass a persistent store (e.g. LibSQLStore, PgStore).',\n\t\t\t);\n\t\t}\n\n\t\t// Resolve tools from all MCP clients.\n\t\tconst mcpToolLists = await Promise.all(this.mcpClients.map(async (c) => await c.listTools()));\n\t\tconst mcpTools = ensureUniqueMcpToolNames(mcpToolLists.flat());\n\t\tconst mcpConnectionFailures = this.getMcpConnectionFailures();\n\n\t\t// Detect collisions between direct, deferred, and MCP tools.\n\t\tconst staticCollisions = findDuplicateToolNames(finalStaticTools);\n\t\tif (staticCollisions.length > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Static tool name collision — the following tool names resolve to duplicates: ${staticCollisions.join(', ')}`,\n\t\t\t);\n\t\t}\n\n\t\tconst staticNames = new Set(finalStaticTools.map((t) => t.name));\n\t\tconst reservedDeferredToolNames = new Set([\n\t\t\tSEARCH_TOOLS_TOOL_NAME,\n\t\t\tLOAD_TOOL_TOOL_NAME,\n\t\t\t...RUNTIME_SKILL_TOOL_NAMES,\n\t\t]);\n\t\tconst deferredNames = new Set<string>();\n\t\tconst deferredCollisions: string[] = [];\n\t\tfor (const tool of finalDeferredTools) {\n\t\t\tif (\n\t\t\t\tstaticNames.has(tool.name) ||\n\t\t\t\treservedDeferredToolNames.has(tool.name) ||\n\t\t\t\tdeferredNames.has(tool.name)\n\t\t\t) {","sourceCodeStart":983,"sourceCodeEnd":1019,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/sdk/agent.ts#L983-L1019","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nagent.tools([\n  { name: 'search', ... },\n  { name: 'search', ... }, // collision\n]);\n// after\nagent.tools([\n  { name: 'web_search', ... },\n  { name: 'db_search', ... },\n]);","handlingStrategy":"validation","validationCode":"function findDuplicates(tools: { name: string }[]): string[] {\n  const seen = new Set<string>();\n  const dupes = new Set<string>();\n  for (const t of tools) {\n    if (seen.has(t.name)) dupes.add(t.name);\n    seen.add(t.name);\n  }\n  return [...dupes];\n}\nconst dupes = findDuplicates(myTools);\nif (dupes.length > 0) {\n  // rename before passing to .tools()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["tools","naming","collision","configuration","agent"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}