{"record":{"id":"0b4927dbb8b14bfc","repo":"spring-projects/spring-ai","slug":"failed-to-extract-all-uri-variables-from-request-u","errorCode":null,"errorMessage":"Failed to extract all URI variables from request URI: {uri}. Expected variables: {expectedVariables}, but found: {foundVariables}","messagePattern":"Failed to extract all URI variables from request URI: (.+?)\\. Expected variables: (.+?), but found: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/SyncMcpResourceMethodCallback.java","lineNumber":123,"sourceCode":"\t * @param request The resource request, must not be null\n\t * @return The resource result\n\t * @throws McpError if there is an error invoking the resource method\n\t * @throws IllegalArgumentException if the request is null or if URI variable\n\t * extraction fails\n\t */\n\t@Override\n\tpublic ReadResourceResult apply(McpSyncServerExchange exchange, ReadResourceRequest request) {\n\t\tif (request == null) {\n\t\t\tthrow new IllegalArgumentException(\"Request must not be null\");\n\t\t}\n\n\t\ttry {\n\t\t\t// Extract URI variable values from the request URI\n\t\t\tMap<String, String> uriVariableValues = this.uriTemplateManager.extractVariableValues(request.uri());\n\n\t\t\t// Verify all URI variables were extracted if URI variables are expected\n\t\t\tif (!this.uriVariables.isEmpty() && uriVariableValues.size() != this.uriVariables.size()) {\n\t\t\t\tthrow new IllegalArgumentException(\"Failed to extract all URI variables from request URI: \"\n\t\t\t\t\t\t+ request.uri() + \". Expected variables: \" + this.uriVariables + \", but found: \"\n\t\t\t\t\t\t+ uriVariableValues.keySet());\n\t\t\t}\n\n\t\t\t// Build arguments for the method call\n\t\t\tObject[] args = this.buildArgs(this.method, exchange, request, uriVariableValues);\n\n\t\t\t// Invoke the method\n\t\t\tthis.method.setAccessible(true);\n\t\t\tObject result = this.method.invoke(this.bean, args);\n\n\t\t\t// Convert the result to a ReadResourceResult using the converter\n\t\t\treturn this.resultConverter.convertToReadResourceResult(result, request.uri(), this.mimeType,\n\t\t\t\t\tthis.contentType, this.meta);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tif (e instanceof McpError mcpError && mcpError.getJsonRpcError() != null) {\n\t\t\t\tthrow mcpError;","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/SyncMcpResourceMethodCallback.java#L105-L141","documentation":"After extracting URI variables from the request URI via UriTemplateManager, apply verifies that the number of extracted values equals the number of URI variables declared on the method's @McpResource URI template. A mismatch means the incoming request URI did not provide values for all declared template variables, so the method cannot be invoked safely and IllegalArgumentException is thrown.","triggerScenarios":"A ReadResourceRequest whose uri() fails to match all variables of the registered template, e.g., template 'wiki://{lang}/{topic}' invoked with 'wiki://topic-only' — one variable missing, sizes differ.","commonSituations":"Client sends a malformed/partial resource URI, URI encoding quirks (e.g., encoded slashes, trailing characters) break extraction, or the template was changed server-side while clients use the old URI shape.","solutions":["Check the request URI matches the full template; ensure every {var} segment has a value.","Verify URI encoding — decode or normalize the URI so variable extraction works (e.g., percent-encoded slashes).","If clients use an older URI shape, keep backward-compatible templates or register an additional resource for the legacy URI.","Log the expected vs. found variable sets to identify which variable failed to extract."],"exampleFix":"// before (request)\nclient.readResource(new McpResourceUri(\"wiki://java\")); // template wiki://{lang}/{topic}\n\n// after\nclient.readResource(new McpResourceUri(\"wiki://java/collections\"));","handlingStrategy":"validation","validationCode":"// verify the URI satisfies the template before calling\nUriTemplateManager mgr = new UriTemplateManager(\"wiki://{lang}/{topic}\");\nMap<String,String> vars = mgr.extractVariableValues(requestUri);\nif (vars.size() != mgr.getVariableNames().size()) {\n    throw new McpError(\"URI \" + requestUri + \" does not match template variables \" + mgr.getVariableNames());\n}","typeGuard":"static boolean uriMatchesAllVars(UriTemplateManager mgr, String uri) {\n    Map<String,String> got = mgr.extractVariableValues(uri);\n    return got != null && got.size() == mgr.getVariableNames().size();\n}","tryCatchPattern":"try {\n    return callback.apply(exchange, request);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Failed to extract all URI variables\")) {\n        log.error(\"Request URI does not satisfy the resource template: \" + request.uri(), e);\n        throw new McpError(\"Invalid resource URI: \" + request.uri());\n    } throw e;\n}","preventionTips":["Test each resource template with a representative fully-populated URI in unit tests.","Careful with percent-encoding (slashes, spaces) in URI variables — normalize before dispatch.","When changing a template, version it or register a legacy alias so old client URIs still match.","Log expected vs. extracted variables on mismatch to debug encoding issues quickly."],"tags":["mcp","uri-template","uri-variables","illegal-argument"],"backgroundTag":"invalid-url-format","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}