alibaba/arthas · error · IllegalArgumentException
Duplicate tool name: ${toolName}
Error message
Duplicate tool name: ${toolName} What it means
Thrown by assertNoDuplicateTool() during the .tools(...) / .tool(...) builder calls when a ToolSpecification name collides with an already-registered regular (non-task) tool. The MCP tools capability requires unique tool names; the check scans the existing this.tools list. It is an IllegalArgumentException at configuration time, before any server starts.
Source
Thrown at arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpServer.java:274
this.taskMessageQueue = taskMessageQueue;
return this;
}
protected void validateTaskConfiguration() {
boolean hasTaskTools = !this.taskTools.isEmpty();
boolean hasTaskStore = this.taskStore != null;
if (hasTaskTools && !hasTaskStore) {
throw new IllegalStateException("Task-aware tools registered but no TaskStore configured. "
+ "Add a TaskStore via .taskStore(store) or remove task tools.");
}
// Note: Having taskStore without taskTools is allowed (for future dynamic registration)
}
private void assertNoDuplicateTool(String toolName) {
for (McpServerFeatures.ToolSpecification tool : this.tools) {
if (tool.getTool().getName().equals(toolName)) {
throw new IllegalArgumentException("Duplicate tool name: " + toolName);
}
}
for (TaskAwareToolSpecification taskTool : this.taskTools) {
if (taskTool.tool().getName().equals(toolName)) {
throw new IllegalArgumentException("Duplicate tool name: " + toolName);
}
}
}
public McpNettyServer build() {
validateTaskConfiguration();
ObjectMapper mapper = this.objectMapper != null ? this.objectMapper : JsonParser.getObjectMapper();
Assert.notNull(this.commandExecutor, "CommandExecutor must be set before building");
return new McpNettyServer(
this.transportProvider, mapper, this.requestTimeout,
new McpServerFeatures.McpServerConfig(this.serverInfo, this.serverCapabilities, this.tools,
this.taskTools,View on GitHub (pinned to 21cf2e9ba5)
Solutions
- Give each ToolSpecification a globally unique name; namespace by module/feature.
- Dedupe the tool list before passing it to the builder.
- Catch IllegalArgumentException during registration setup to fail fast with a clear message.
- Audit all .tool()/.tools() call sites for name collisions.
Example fix
// before
spec.tool(toolNamedExecute).tool(otherToolNamedExecute); // throws
// after
Set<String> names = new HashSet<>();
for (var t : allTools) {
if (!names.add(t.getTool().getName())) {
throw new IllegalStateException("Duplicate: " + t.getTool().getName());
}
}
spec.tools(allTools); Defensive patterns
Strategy: validation
Validate before calling
Set<String> names = new HashSet<>();
for (McpServerFeatures.ToolSpecification t : allTools) {
if (!names.add(t.getTool().getName())) {
throw new IllegalStateException("Duplicate tool name: " + t.getTool().getName());
}
}
spec.tools(allTools); Try / catch
try {
spec.tool(newTool);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Duplicate tool name")) {
// rename or skip
} else throw e;
} Prevention
- Dedupe the tool list before registering.
- Namespace tool names per module.
- Unit-test that your tool registry has unique names.
When it happens
Trigger: Adding two ToolSpecifications whose getTool().getName() returns the same string via .tools(...) or .tool(...); merging tool lists from modules that did not coordinate names.
Common situations: Two plugins defining a tool named identically (e.g. 'execute'); copy-pasted tool specs; default tool names colliding with custom ones.
Related errors
- Task-aware tools registered but no TaskStore configured. Add
- Tool with name '{}' already exists
- Prompt with name '${promptName}' already exists
- Tool with name '{}' not found
- Resource with URI '{}' already exists
AI-assisted analysis of alibaba/arthas@21cf2e9ba5 (2026-08-14).
Data as JSON: /api/errors/9448452d5b679852.
Report an issue: GitHub.