alibaba/arthas · error · McpError

Prompt with name '{}' already exists

Error message

Prompt with name '{}' already exists

What it means

Thrown by McpStatelessNettyServer.addPrompt() when dynamically registering a PromptSpecification whose prompt name already exists in the stateless server's prompts map (putIfAbsent returns non-null). The async add fails the returned CompletableFuture with this McpError wrapped in CompletionException.

Source

Thrown at arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpStatelessNettyServer.java:384

	public CompletableFuture<Void> addPrompt(McpStatelessServerFeatures.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
				.runAsync(() -> {
					String name = promptSpecification.getPrompt().getName();
					McpStatelessServerFeatures.PromptSpecification existing =
							this.prompts.putIfAbsent(name, promptSpecification);
					if (existing != null) {
						throw new CompletionException(new McpError("Prompt with name '" + name + "' already exists"));
					}
					logger.debug("Added prompt handler: {}", name);
				})
				.exceptionally(ex -> {
					Throwable cause = (ex instanceof CompletionException) ? ex.getCause() : ex;
					String name = promptSpecification.getPrompt().getName();
					logger.error("Error while adding prompt '{}'", name, cause);
					throw new CompletionException(cause);
				});
	}

	public CompletableFuture<Void> removePrompt(String promptName) {
		if (promptName == null || promptName.isEmpty()) {
			CompletableFuture<Void> future = new CompletableFuture<>();
			future.completeExceptionally(new McpError("Prompt name must not be null or empty"));
			return future;
		}
		if (this.serverCapabilities.getPrompts() == null) {

View on GitHub (pinned to 21cf2e9ba5)

Solutions

  1. removePrompt first (idempotently) to upsert, or check current prompts before adding.
  2. Use unique namespaced prompt names.
  3. Catch CompletionException/McpError and skip on duplicate.
  4. Coordinate prompt registration to prevent concurrent duplicate adds.

Example fix

// before
statelessServer.addPrompt(spec).join(); // name exists

// after
String name = spec.getPrompt().getName();
statelessServer.removePrompt(name).exceptionally(ex -> null).join();
statelessServer.addPrompt(spec).join();
Defensive patterns

Strategy: validation

Validate before calling

String name = spec.getPrompt().getName();
statelessServer.removePrompt(name).exceptionally(ex -> null).join();
statelessServer.addPrompt(spec).join();

Try / catch

try {
    statelessServer.addPrompt(spec).join();
} catch (CompletionException e) {
    Throwable c = e.getCause();
    if (c instanceof McpError me && me.getMessage().contains("already exists")) {
        statelessServer.removePrompt(spec.getPrompt().getName())
            .exceptionally(x -> null).join();
        statelessServer.addPrompt(spec).join();
    } else throw e;
}

Prevention

When it happens

Trigger: Calling addPrompt(spec) at runtime when getPrompt().getName() collides with an existing prompt; re-adding after a successful add.

Common situations: Live prompt re-registration; plugin load colliding with an existing prompt name; retry-based double add.

Related errors


AI-assisted analysis of alibaba/arthas@21cf2e9ba5 (2026-08-14). Data as JSON: /api/errors/8205ac7a967e4d24. Report an issue: GitHub.