n8n-io/n8n · error · Error

Deferred tool name collision — the following tool names reso

Error message

Deferred tool name collision — the following tool names resolve to duplicates or reserved tools: ${deferredCollisions.join(', ')}

What it means

Thrown by Agent.build() when a deferred tool name collides with an existing static tool name, another deferred tool name, or a reserved tool name (SEARCH_TOOLS_TOOL_NAME, LOAD_TOOL_TOOL_NAME, or any RUNTIME_SKILL_TOOL_NAMES). Deferred tools are loaded on demand, so name conflicts are checked against the full namespace to prevent ambiguity at call time.

Source

Thrown at packages/@n8n/agents/src/sdk/agent.ts:1025

		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)
			) {
				deferredCollisions.push(tool.name);
			}
			deferredNames.add(tool.name);
		}
		if (deferredCollisions.length > 0) {
			throw new Error(
				`Deferred tool name collision — the following tool names resolve to duplicates or reserved tools: ${deferredCollisions.join(', ')}`,
			);
		}

		const collisions = mcpTools
			.filter((t) => staticNames.has(t.name) || deferredNames.has(t.name))
			.map((t) => t.name);
		if (collisions.length > 0) {
			throw new Error(
				`MCP tool name collision — the following tool names resolve to duplicates: ${collisions.join(', ')}`,
			);
		}

		let allTools = [...finalStaticTools, ...mcpTools];

		// Validate checkpoint again after discovering actual MCP tools
		// (catches the case where MCP tools have suspendSchema after listing).
		const allNeedCheckpoint =

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Rename the colliding deferred tool to a unique name not present in static, reserved, or other deferred tools.
  2. Check the error message which lists the colliding names and resolve each one.
  3. Avoid using reserved names (check RUNTIME_SKILL_TOOL_NAMES, SEARCH_TOOLS_TOOL_NAME, LOAD_TOOL_TOOL_NAME).

Example fix

// before
agent.tools([{ name: 'fetch', ... }])
     .deferredTools([{ name: 'fetch', factory: ... }]); // collision
// after
agent.tools([{ name: 'fetch', ... }])
     .deferredTools([{ name: 'lazy_fetch', factory: ... }]);
Defensive patterns

Strategy: validation

Validate before calling

const RESERVED = new Set([SEARCH_TOOLS_TOOL_NAME, LOAD_TOOL_TOOL_NAME, ...RUNTIME_SKILL_TOOL_NAMES]);
function findDeferredCollisions(staticTools: { name: string }[], deferredTools: { name: string }[]): string[] {
  const staticNames = new Set(staticTools.map((t) => t.name));
  const seen = new Set<string>();
  const collisions: string[] = [];
  for (const t of deferredTools) {
    if (staticNames.has(t.name) || RESERVED.has(t.name) || seen.has(t.name)) collisions.push(t.name);
    seen.add(t.name);
  }
  return collisions;
}

Prevention

When it happens

Trigger: Registering a deferred tool whose name matches a static tool, another deferred tool, or a reserved runtime name. Deferred tools are added via the deferred tool registration path and checked against staticNames, reservedDeferredToolNames, and deferredNames sets.

Common situations: A deferred tool factory generating a name that overlaps with a static tool added in the same build. Using a reserved name like 'search_tools' for a deferred tool. Combining deferred tools from multiple modules with overlapping names.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/d4afaa4e80f7b959. Report an issue: GitHub.