alibaba/arthas · error · McpError
Prompt with name '${promptName}' already exists
Error message
Prompt with name '${promptName}' already exists What it means
Thrown by McpNettyServer.addPrompt() when a PromptSpecification whose prompt name already exists in the server's prompts map is registered again. Registration uses putIfAbsent, so a second add of the same name fails the returned future with this McpError instead of silently overwriting. Prompt names are the unique key for the MCP prompts capability.
Source
Thrown at arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpNettyServer.java:539
// ---------------------------------------
public CompletableFuture<Void> addPrompt(McpServerFeatures.PromptSpecification promptSpecification) {
if (promptSpecification == null) {
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new McpError("Prompt specification must not be null"));
return future;
}
if (this.serverCapabilities.getPrompts() == null) {
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new McpError("Server must be configured with prompt capabilities"));
return future;
}
return CompletableFuture.supplyAsync(() -> {
McpServerFeatures.PromptSpecification existing = this.prompts
.putIfAbsent(promptSpecification.getPrompt().getName(), promptSpecification);
if (existing != null) {
throw new CompletionException(
new McpError(
"Prompt with name '" + promptSpecification.getPrompt().getName() + "' already exists"));
}
logger.debug("Added prompt handler: {}", promptSpecification.getPrompt().getName());
return null;
}).thenCompose(ignored -> {
if (this.serverCapabilities.getPrompts().getListChanged()) {
return notifyPromptsListChanged();
}
return CompletableFuture.completedFuture(null);
}).exceptionally(ex -> {
Throwable cause = (ex instanceof CompletionException) ? ex.getCause() : ex;
logger.error("Error while adding prompt '{}'", promptSpecification.getPrompt().getName(), cause);
throw new CompletionException(cause);
});
}
View on GitHub (pinned to 21cf2e9ba5)
Solutions
- Use unique prompt names per PromptSpecification; namespace them (e.g. 'plugin.promptName').
- Before addPrompt, check the existing set or call removePrompt first if you intend to replace.
- Centralize prompt registration in one place to avoid duplicate adds from different modules.
- Catch the CompletionException/McpError and log/skip on conflict rather than failing the whole startup.
Example fix
// before server.addPrompt(duplicatePromptSpec).join(); // 'review' already exists // after String name = spec.getPrompt().getName(); server.removePrompt(name).exceptionally(ex -> null).join(); // ignore if absent server.addPrompt(spec).join();
Defensive patterns
Strategy: validation
Validate before calling
// Only add a prompt if the name is not already registered
String name = spec.getPrompt().getName();
boolean absent = currentPromptNames.add(name); // shared ConcurrentHashMap.newKeySet()
if (absent) {
server.addPrompt(spec).join();
} Try / catch
try {
server.addPrompt(spec).join();
} catch (CompletionException e) {
if (e.getCause() instanceof McpError me && me.getMessage().contains("already exists")) {
// upsert: remove then re-add
server.removePrompt(spec.getPrompt().getName())
.exceptionally(x -> null).join();
server.addPrompt(spec).join();
} else throw e;
} Prevention
- Namespace prompt names to avoid cross-module collisions.
- Centralize prompt registration in one component.
- For replacement semantics, removePrompt first (ignoring absent).
When it happens
Trigger: Calling addPrompt with a spec whose getPrompt().getName() collides with an already-registered prompt; re-running registration logic on startup without clearing prior state; adding the same prompt from two modules.
Common situations: Hot-reload/restart of registration code while the server object persists; two plugins registering a prompt named the same (e.g. 'review'); copy-paste of a PromptSpecification without renaming.
Related errors
- Prompt with name '${promptName}' not found
- Prompt with name '{}' already exists
- Resource with URI '${resourceUri}' not found
- Tool with name '{}' already exists
- Resource with URI '{}' already exists
AI-assisted analysis of alibaba/arthas@21cf2e9ba5 (2026-08-14).
Data as JSON: /api/errors/da7e145b121e896f.
Report an issue: GitHub.