{"record":{"id":"7dd4bbaa8f6beb46","repo":"alibaba/arthas","slug":"resource-with-uri-resourceuri-not-found","errorCode":null,"errorMessage":"Resource with URI '${resourceUri}' not found","messagePattern":"Resource with URI '(.+?)' not found","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":467,"sourceCode":"\t\t});\n\t}\n\n\tpublic CompletableFuture<Void> removeResource(String resourceUri) {\n\t\tif (resourceUri == null) {\n\t\t\tCompletableFuture<Void> future = new CompletableFuture<>();\n\t\t\tfuture.completeExceptionally(new McpError(\"Resource URI must not be null\"));\n\t\t\treturn future;\n\t\t}\n\t\tif (this.serverCapabilities.getResources() == null) {\n\t\t\tCompletableFuture<Void> future = new CompletableFuture<>();\n\t\t\tfuture.completeExceptionally(new McpError(\"Server must be configured with resource capabilities\"));\n\t\t\treturn future;\n\t\t}\n\n\t\treturn CompletableFuture.supplyAsync(() -> {\n\t\t\tMcpServerFeatures.ResourceSpecification removed = this.resources.remove(resourceUri);\n\t\t\tif (removed == null) {\n\t\t\t\tthrow new CompletionException(new McpError(\"Resource with URI '\" + resourceUri + \"' not found\"));\n\t\t\t}\n\n\t\t\tlogger.debug(\"Removed resource handler: {}\", resourceUri);\n\t\t\treturn null;\n\t\t}).thenCompose(ignored -> {\n\t\t\tif (this.serverCapabilities.getResources().getListChanged()) {\n\t\t\t\treturn notifyResourcesListChanged();\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 removing resource '{}'\", resourceUri, cause);\n\t\t\tthrow new CompletionException(cause);\n\t\t});\n\t}\n\n\tpublic CompletableFuture<Void> notifyResourcesListChanged() {\n\t\treturn this.mcpTransportProvider.notifyClients(McpSchema.METHOD_NOTIFICATION_RESOURCES_LIST_CHANGED, null);","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/alibaba/arthas/blob/21cf2e9ba52b305290be7223b980ff504bb9cb5b/arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/protocol/server/McpNettyServer.java#L449-L485","documentation":"Thrown by McpNettyServer.removeResource() when you try to dynamically unregister a resource whose URI is not currently registered. The server holds resources in a name-keyed map; a removal that finds no existing entry fails the returned CompletableFuture with this McpError (wrapped in a CompletionException). It is a stateful-server runtime error, distinct from the resource-capabilities-missing guard that runs before it.","triggerScenarios":"Calling removeResource(resourceUri) where resourceUri was never added, was already removed, or has expired. Also when the URI passed does not byte-match the key used at registration (trailing slash, case, scheme).","commonSituations":"Double removal of the same resource; removal on a server instance that did not register the resource (e.g. wrong server or session); cleanup code running after startup registration changed order; URI normalization mismatch between add and remove.","solutions":["Track registered resource URIs in your own set and only call removeResource for URIs you know are present.","Verify the exact URI string passed to addResource is reused verbatim for removal (no trimming/normalization).","Catch CompletionException/McpError on the returned future and treat 'not found' as a no-op for idempotent cleanup.","Confirm you are operating on the same McpNettyServer instance that owns the resource."],"exampleFix":"// before\nserver.removeResource(uri).join(); // throws if absent\n\n// after\nSet<String> registered = ConcurrentHashMap.newKeySet();\nserver.addResource(spec);\nregistered.add(uri);\n// ...\nif (registered.remove(uri)) {\n    server.removeResource(uri).join();\n}","handlingStrategy":"validation","validationCode":"// Track URIs you registered and guard removal\nSet<String> registeredResources = ConcurrentHashMap.newKeySet();\n// on add: registeredResources.add(uri);\nif (registeredResources.remove(resourceUri)) {\n    server.removeResource(resourceUri).join();\n}","typeGuard":null,"tryCatchPattern":"// Idempotent removal: swallow 'not found'\nserver.removeResource(resourceUri)\n    .exceptionally(ex -> {\n        if (isMcpError(ex, \"not found\")) return null;\n        throw new CompletionException(ex instanceof CompletionException\n            ? ex.getCause() : ex);\n    })\n    .join();","preventionTips":["Keep your own set of registered URIs and remove only those.","Reuse the exact URI string from registration for removal.","Make cleanup code idempotent by treating 'not found' as success."],"tags":["mcp","resource","dynamic-registration","stateful-server","java"],"backgroundTag":null,"analyzedSha":"21cf2e9ba52b305290be7223b980ff504bb9cb5b","analyzedAt":"2026-08-14T00:57:07.243Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}