n8n-io/n8n · error · Error

Tool name "${reservedTool.name}" is reserved for runtime ski

Error message

Tool name "${reservedTool.name}" is reserved for runtime skills

What it means

Thrown by Agent.skills() when an existing tool in the agent's tool list has a name that is in the RUNTIME_SKILL_TOOL_NAMES reserved set. The library reserves specific tool names for its own runtime skill management tools (search_tools, load_tool), and registering a user tool with one of these names would shadow the runtime's ability to discover and load skills.

Source

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

	}

	/**
	 * Add runtime-loadable skills to the agent. The model sees only a compact
	 * name/description catalog in the system prompt, then calls `load_skill`
	 * to retrieve the full instructions for a relevant skill.
	 */
	skills(sourceOrSkills: RuntimeSkillSource | RuntimeSkill[]): this {
		const source = Array.isArray(sourceOrSkills)
			? createRuntimeSkillSource(sourceOrSkills)
			: sourceOrSkills;

		this.removeRuntimeSkillTools();
		this.skillSource = source;
		if (source.registry.skills.length === 0) return this;

		const reservedTool = this.tools.find((tool) => RUNTIME_SKILL_TOOL_NAMES.has(tool.name));
		if (reservedTool) {
			throw new Error(`Tool name "${reservedTool.name}" is reserved for runtime skills`);
		}

		this.tools.push(...createRuntimeSkillTools(source));
		this.hasRuntimeSkillTool = true;
		return this;
	}

	/** Add a provider-defined tool (e.g. Anthropic web search, OpenAI code interpreter). */
	providerTool(builtProviderTool: BuiltProviderTool): this {
		this.providerTools.push(builtProviderTool);
		return this;
	}

	/** Read the declared tools. Lists only tools added via tool() */
	get declaredTools(): BuiltTool[] {
		return this.tools;
	}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Rename the conflicting tool to a non-reserved name before calling .skills().
  2. Check the RUNTIME_SKILL_TOOL_NAMES set (exported from the package) before naming custom tools.
  3. Add skills before adding custom tools, so you know which names are taken.

Example fix

// before
agent.tools([myTool]).skills(skillSource); // myTool.name === 'search_tools'
// after
myTool.name = 'my_search';
agent.tools([myTool]).skills(skillSource);
Defensive patterns

Strategy: validation

Validate before calling

import { RUNTIME_SKILL_TOOL_NAMES } from '@n8n/agents';
function hasReservedToolName(tools: { name: string }[]): boolean {
  return tools.some((t) => RUNTIME_SKILL_TOOL_NAMES.has(t.name));
}
if (hasReservedToolName(myTools)) {
  // rename before calling .skills()
}

Type guard

function isReservedSkillToolName(name: string, reserved: Set<string>): name is string {
  return reserved.has(name);
}

Prevention

When it happens

Trigger: Calling agent.skills(source) after having added a tool via .tools() whose name matches one of the reserved runtime skill tool names. Or calling .tools() with a reserved name and then activating skills.

Common situations: Naming a custom tool 'search_tools' or 'load_tool' (or whatever names are in RUNTIME_SKILL_TOOL_NAMES). Importing tools from another library that happens to use a reserved name. Copying tool names from documentation without checking against the reserved set.

Related errors


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