{"record":{"id":"da7e145b121e896f","repo":"alibaba/arthas","slug":"prompt-with-name-promptname-already-exists","errorCode":null,"errorMessage":"Prompt with name '${promptName}' already exists","messagePattern":"Prompt with name '(.+?)' already exists","errorType":"exception","errorClass":"McpError","httpStatus":null,"severity":"error","filePath":"arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpNettyServer.java","lineNumber":539,"sourceCode":"\t// ---------------------------------------\n\n\tpublic CompletableFuture<Void> addPrompt(McpServerFeatures.PromptSpecification promptSpecification) {\n\t\tif (promptSpecification == null) {\n\t\t\tCompletableFuture<Void> future = new CompletableFuture<>();\n\t\t\tfuture.completeExceptionally(new McpError(\"Prompt specification must not be null\"));\n\t\t\treturn future;\n\t\t}\n\t\tif (this.serverCapabilities.getPrompts() == null) {\n\t\t\tCompletableFuture<Void> future = new CompletableFuture<>();\n\t\t\tfuture.completeExceptionally(new McpError(\"Server must be configured with prompt capabilities\"));\n\t\t\treturn future;\n\t\t}\n\n\t\treturn CompletableFuture.supplyAsync(() -> {\n\t\t\tMcpServerFeatures.PromptSpecification existing = this.prompts\n\t\t\t\t\t.putIfAbsent(promptSpecification.getPrompt().getName(), promptSpecification);\n\t\t\tif (existing != null) {\n\t\t\t\tthrow new CompletionException(\n\t\t\t\t\t\tnew McpError(\n\t\t\t\t\t\t\t\t\"Prompt with name '\" + promptSpecification.getPrompt().getName() + \"' already exists\"));\n\t\t\t}\n\n\t\t\tlogger.debug(\"Added prompt handler: {}\", promptSpecification.getPrompt().getName());\n\t\t\treturn null;\n\t\t}).thenCompose(ignored -> {\n\t\t\tif (this.serverCapabilities.getPrompts().getListChanged()) {\n\t\t\t\treturn notifyPromptsListChanged();\n\t\t\t}\n\t\t\treturn CompletableFuture.completedFuture(null);\n\t\t}).exceptionally(ex -> {\n\t\t\tThrowable cause = (ex instanceof CompletionException) ? ex.getCause() : ex;\n\t\t\tlogger.error(\"Error while adding prompt '{}'\", promptSpecification.getPrompt().getName(), cause);\n\t\t\tthrow new CompletionException(cause);\n\t\t});\n\t}\n","sourceCodeStart":521,"sourceCodeEnd":557,"githubUrl":"https://github.com/alibaba/arthas/blob/21cf2e9ba52b305290be7223b980ff504bb9cb5b/arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpNettyServer.java#L521-L557","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nserver.addPrompt(duplicatePromptSpec).join(); // 'review' already exists\n\n// after\nString name = spec.getPrompt().getName();\nserver.removePrompt(name).exceptionally(ex -> null).join(); // ignore if absent\nserver.addPrompt(spec).join();","handlingStrategy":"validation","validationCode":"// Only add a prompt if the name is not already registered\nString name = spec.getPrompt().getName();\nboolean absent = currentPromptNames.add(name); // shared ConcurrentHashMap.newKeySet()\nif (absent) {\n    server.addPrompt(spec).join();\n}","typeGuard":null,"tryCatchPattern":"try {\n    server.addPrompt(spec).join();\n} catch (CompletionException e) {\n    if (e.getCause() instanceof McpError me && me.getMessage().contains(\"already exists\")) {\n        // upsert: remove then re-add\n        server.removePrompt(spec.getPrompt().getName())\n            .exceptionally(x -> null).join();\n        server.addPrompt(spec).join();\n    } else throw e;\n}","preventionTips":["Namespace prompt names to avoid cross-module collisions.","Centralize prompt registration in one component.","For replacement semantics, removePrompt first (ignoring absent)."],"tags":["mcp","prompt","dynamic-registration","duplicate","stateful-server","java"],"backgroundTag":null,"analyzedSha":"21cf2e9ba52b305290be7223b980ff504bb9cb5b","analyzedAt":"2026-08-14T00:57:07.243Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}