alibaba/arthas · error · McpError
Resource with URI '${resourceUri}' already exists
Error message
Resource with URI '${resourceUri}' already exists What it means
McpNettyServer.addResource throws McpError (wrapped in CompletionException) when a resource with the same URI is already registered. Resources are stored in a map keyed by Resource.getUri(), and putIfAbsent returning a non-null value means the URI is taken; duplicate URIs would otherwise overwrite each other.
Source
Thrown at arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpNettyServer.java:435
// Resource Management
// ---------------------------------------
public CompletableFuture<Void> addResource(McpServerFeatures.ResourceSpecification resourceSpecification) {
if (resourceSpecification == null || resourceSpecification.getResource() == null) {
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new McpError("Resource must not be null"));
return future;
}
if (this.serverCapabilities.getResources() == null) {
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new McpError("Server must be configured with resource capabilities"));
return future;
}
return CompletableFuture.supplyAsync(() -> {
if (this.resources.putIfAbsent(resourceSpecification.getResource().getUri(),
resourceSpecification) != null) {
throw new CompletionException(new McpError(
"Resource with URI '" + resourceSpecification.getResource().getUri() + "' already exists"));
}
logger.debug("Added resource handler: {}", resourceSpecification.getResource().getUri());
return null;
}).thenCompose(ignored -> {
if (this.serverCapabilities.getResources().getListChanged()) {
return notifyResourcesListChanged();
}
return CompletableFuture.completedFuture(null);
}).exceptionally(ex -> {
Throwable cause = ex instanceof CompletionException ? ex.getCause() : ex;
logger.error("Error while adding resource '{}'", resourceSpecification.getResource().getUri(), cause);
throw new CompletionException(cause);
});
}
public CompletableFuture<Void> removeResource(String resourceUri) {
if (resourceUri == null) {View on GitHub (pinned to 21cf2e9ba5)
Solutions
- Call removeResource(uri) before addResource when refreshing.
- Check the existing resources map for the URI before adding.
- Ensure each resource exposes a unique URI.
Example fix
// before
server.addResource(spec); // uri 'jfr://file/123' already exists
// after
server.removeResource("jfr://file/123")
.thenRun(() -> server.addResource(spec)); Defensive patterns
Strategy: validation
Validate before calling
String uri = spec.getResource().getUri();
boolean exists = server.getResources().stream()
.anyMatch(r -> r.getResource().getUri().equals(uri));
if (exists) server.removeResource(uri).join();
server.addResource(spec).join(); Try / catch
try { server.addResource(spec).join(); }
catch (CompletionException e) {
if (e.getCause() instanceof io.modelcontextprotocol.spec.McpError && e.getCause().getMessage().contains("already exists")) { server.removeResource(uri).thenRun(() -> server.addResource(spec)).join(); }
else throw e;
} Prevention
- Check resources map before addResource.
- removeResource before re-adding on reload.
- Give each resource a unique URI.
When it happens
Trigger: Calling addResource twice with resources whose getUri() is identical; hot reload without removeResource first; two providers contributing the same resource URI.
Common situations: Dynamic resource registration on reconnect; resource discovery registering the same URI across modules; URI normalization differences leading to an unexpected exact collision.
Related errors
- Tool with name '${toolName}' already exists
- Tool with name '${toolName}' not found
- Resource with URI '{}' already exists
- Resource with URI '${resourceUri}' not found
- Prompt with name '${promptName}' already exists
AI-assisted analysis of alibaba/arthas@21cf2e9ba5 (2026-08-14).
Data as JSON: /api/errors/15910fcf447feb54.
Report an issue: GitHub.