{"record":{"id":"349c6b89eeb066e6","repo":"n8n-io/n8n","slug":"duplicate-deferred-tool-name-tool-name","errorCode":null,"errorMessage":"Duplicate deferred tool name \"${tool.name}\"","messagePattern":"Duplicate deferred tool name \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/runtime/tools/deferred-tool-manager.ts","lineNumber":96,"sourceCode":"\nexport class DeferredToolManager {\n\tprivate readonly toolsByName = new Map<string, BuiltTool>();\n\n\tprivate readonly loadedToolNames = new Set<string>();\n\n\tprivate readonly topK: number;\n\n\tprivate readonly searchTool: BuiltTool;\n\n\tprivate readonly loadTool: BuiltTool;\n\n\tconstructor(tools: BuiltTool[], options: DeferredToolManagerOptions = {}) {\n\t\tfor (const tool of tools) {\n\t\t\tif (tool.name === SEARCH_TOOLS_TOOL_NAME || tool.name === LOAD_TOOL_TOOL_NAME) {\n\t\t\t\tthrow new Error(`Deferred tool name \"${tool.name}\" is reserved`);\n\t\t\t}\n\t\t\tif (this.toolsByName.has(tool.name)) {\n\t\t\t\tthrow new Error(`Duplicate deferred tool name \"${tool.name}\"`);\n\t\t\t}\n\t\t\tthis.toolsByName.set(tool.name, tool);\n\t\t}\n\n\t\tthis.topK = options.topK ?? DEFAULT_TOP_K;\n\t\tthis.searchTool = this.createSearchTool();\n\t\tthis.loadTool = this.createLoadTool();\n\t}\n\n\tget hasTools(): boolean {\n\t\treturn this.toolsByName.size > 0;\n\t}\n\n\tget totalToolCount(): number {\n\t\treturn this.toolsByName.size;\n\t}\n\n\tget loadedToolCount(): number {","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/runtime/tools/deferred-tool-manager.ts#L78-L114","documentation":"`DeferredToolManager` indexes tools by name in a `Map` for O(1) lookup by `load_tool`. If two tools in the constructor array share the same `name`, the second would overwrite the first, silently hiding a tool. The constructor detects this and throws to fail fast rather than silently lose a tool.","triggerScenarios":"Passing `new DeferredToolManager([toolA, toolB])` where `toolA.name === toolB.name`. Common when tools are aggregated from multiple sources (MCP servers, built-in catalog, custom tools) and two sources define a tool with the same name.","commonSituations":"Two MCP servers expose a tool with the same name (e.g. both have `read_file`). A built-in tool and a custom tool share a name. A tool list was accidentally duplicated during assembly.","solutions":["Identify the duplicate name from the error message.","Rename one of the conflicting tools, or deduplicate by keeping only the intended one.","Implement a namespacing/prefixing strategy when merging tools from multiple sources (e.g. prefix with the MCP server name).","Add a pre-construction deduplication step: build a `Map<string, BuiltTool>` and handle collisions before passing to the manager."],"exampleFix":"// before:\nconst mgr = new DeferredToolManager([\n  { name: 'read_file', ... },  // from MCP server A\n  { name: 'read_file', ... },  // from MCP server B\n]);\n\n// after: namespace by source\nconst mgr = new DeferredToolManager([\n  { name: 'serverA__read_file', ... },\n  { name: 'serverB__read_file', ... },\n]);","handlingStrategy":"validation","validationCode":"function deduplicateTools(tools: BuiltTool[]): BuiltTool[] {\n  const seen = new Map<string, BuiltTool>();\n  for (const tool of tools) {\n    if (seen.has(tool.name)) {\n      logger.warn(`Duplicate tool name \"${tool.name}\", keeping first`);\n      continue;\n    }\n    seen.set(tool.name, tool);\n  }\n  return [...seen.values()];\n}\n\nconst mgr = new DeferredToolManager(deduplicateTools(allTools));","typeGuard":"function hasUniqueNames(tools: BuiltTool[]): boolean {\n  const names = tools.map((t) => t.name);\n  return new Set(names).size === names.length;\n}","tryCatchPattern":null,"preventionTips":["Deduplicate tools by name before constructing DeferredToolManager.","Namespace tools from different sources (e.g. `serverA__read_file`).","Validate tool name uniqueness at assembly time."],"tags":["tools","deferred-tools","validation","duplicate","naming"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}