alibaba/arthas · error · McpError

Resource with URI ' ' already exists

Error message

Resource with URI '{}' already exists

What it means

Thrown by McpStatelessNettyServer.addResource() when dynamically registering a resource whose URI already exists in the resources map (putIfAbsent returns non-null). The async add fails the returned CompletableFuture with this McpError wrapped in CompletionException. URIs are the unique key for the resources capability.

Solutions

  1. Check or removeResource before addResource to implement upsert semantics.
  2. Use unique, namespaced URIs for dynamically added resources.
  3. Catch CompletionException/McpError on the future and skip on duplicate.
  4. Centralize resource registration to avoid concurrent duplicate adds.

Example fix

// before
statelessServer.addResource(spec).join(); // uri exists

// after
String uri = spec.getResource().getUri();
statelessServer.removeResource(uri).exceptionally(ex -> null).join();
statelessServer.addResource(spec).join();
Defensive patterns

Strategy: validation

Validate before calling

String uri = spec.getResource().getUri();
statelessServer.removeResource(uri).exceptionally(ex -> null).join();
statelessServer.addResource(spec).join();

Try / catch

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

Prevention

When it happens

Trigger: Calling addResource(spec) at runtime when resourceSpecification.getResource().getUri() collides with an existing resource; re-adding after a prior add succeeded.

Common situations: Hot re-registration of resources; two plugins registering the same URI (e.g. 'config://app'); retry logic that double-adds.

Related errors


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

Appendix: source

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

	// ---------------------------------------

	public CompletableFuture<Void> addResource(McpStatelessServerFeatures.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
				.runAsync(() -> {
					String uri = resourceSpecification.getResource().getUri();
					if (this.resources.putIfAbsent(uri, resourceSpecification) != null) {
						throw new CompletionException(new McpError("Resource with URI '" + uri + "' already exists"));
					}
					logger.debug("Added resource handler: {}", uri);
				})
				.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) {
			CompletableFuture<Void> future = new CompletableFuture<>();
			future.completeExceptionally(new McpError("Resource URI must not be null"));
			return future;
		}
		if (this.serverCapabilities.getResources() == null) {

View on GitHub (pinned to 21cf2e9ba5)