{"record":{"id":"6931ba03c7c39c0d","repo":"spring-projects/spring-ai","slug":"failed-to-extract-all-uri-variables-from-request-u-6931ba","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/SyncStatelessMcpResourceMethodCallback.java","lineNumber":118,"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(McpTransportContext context, 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, context, 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":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/SyncStatelessMcpResourceMethodCallback.java#L100-L136","documentation":"The request URI did not yield values for all URI template variables declared on the @McpResource method. The callback compares the extracted variable count to the declared variables and throws when they differ.","triggerScenarios":"A ReadResourceRequest whose URI does not match the method's URI template (e.g. missing a path segment or query part that maps to a declared {var}), so extractVariableValues returns fewer variables than declared.","commonSituations":"Client calling the wrong resource URI; typos in URI template placeholders; registering a template with variables the caller omits; URI-encoding differences that break extraction.","solutions":["Send a request URI that fully matches the @McpResource uri template, filling every {variable}","Verify the template placeholders match the method parameter names (this.uriVariables)","Normalize/decode the request URI before it reaches the callback in custom transports","Add integration tests that exercise the templated URI end to end"],"exampleFix":"// before\nvar result = client.readResource(\"file:///docs\"); // template is file://{path}\n// after\nvar result = client.readResource(\"file:///docs/report.txt\");","handlingStrategy":"validation","validationCode":"Set<String> declared = Set.of(\"path\"); // from the @McpResource template\nMap<String,String> vars = uriTemplateManager.extractVariableValues(request.uri());\nif (vars.size() != declared.size()) {\n    throw new IllegalArgumentException(\"URI \" + request.uri() + \" does not fill all template variables\");\n}","typeGuard":"boolean uriMatchesTemplate(String uri, String template) {\n    return uri != null && !uri.isBlank() && uri.startsWith(template.substring(0, template.indexOf('{')));\n}","tryCatchPattern":"try {\n    return callback.apply(context, request);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Failed to extract all URI variables\")) {\n        return errorResult(\"Resource URI does not match template\");\n    }\n    throw e;\n}","preventionTips":["Test each @McpResource template with a fully filled example URI","Keep placeholder names aligned with method parameters","URL-decode requests before extraction in custom transports"],"tags":["mcp","java","uri-template","variables"],"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-14T11:17:12.474Z"}