spring-projects/spring-ai · error · IllegalArgumentException
Failed to extract all URI variables from request URI: {uri}.
Error message
Failed to extract all URI variables from request URI: {uri}. Expected variables: {expectedVariables}, but found: {foundVariables} What it means
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.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/SyncMcpResourceMethodCallback.java:123
* @param request The resource request, must not be null
* @return The resource result
* @throws McpError if there is an error invoking the resource method
* @throws IllegalArgumentException if the request is null or if URI variable
* extraction fails
*/
@Override
public ReadResourceResult apply(McpSyncServerExchange exchange, ReadResourceRequest request) {
if (request == null) {
throw new IllegalArgumentException("Request must not be null");
}
try {
// Extract URI variable values from the request URI
Map<String, String> uriVariableValues = this.uriTemplateManager.extractVariableValues(request.uri());
// Verify all URI variables were extracted if URI variables are expected
if (!this.uriVariables.isEmpty() && uriVariableValues.size() != this.uriVariables.size()) {
throw new IllegalArgumentException("Failed to extract all URI variables from request URI: "
+ request.uri() + ". Expected variables: " + this.uriVariables + ", but found: "
+ uriVariableValues.keySet());
}
// Build arguments for the method call
Object[] args = this.buildArgs(this.method, exchange, request, uriVariableValues);
// Invoke the method
this.method.setAccessible(true);
Object result = this.method.invoke(this.bean, args);
// Convert the result to a ReadResourceResult using the converter
return this.resultConverter.convertToReadResourceResult(result, request.uri(), this.mimeType,
this.contentType, this.meta);
}
catch (Exception e) {
if (e instanceof McpError mcpError && mcpError.getJsonRpcError() != null) {
throw mcpError;View on GitHub (pinned to 98a7beda4f)
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.
Example fix
// before (request)
client.readResource(new McpResourceUri("wiki://java")); // template wiki://{lang}/{topic}
// after
client.readResource(new McpResourceUri("wiki://java/collections")); Defensive patterns
Strategy: validation
Validate before calling
// verify the URI satisfies the template before calling
UriTemplateManager mgr = new UriTemplateManager("wiki://{lang}/{topic}");
Map<String,String> vars = mgr.extractVariableValues(requestUri);
if (vars.size() != mgr.getVariableNames().size()) {
throw new McpError("URI " + requestUri + " does not match template variables " + mgr.getVariableNames());
} Type guard
static boolean uriMatchesAllVars(UriTemplateManager mgr, String uri) {
Map<String,String> got = mgr.extractVariableValues(uri);
return got != null && got.size() == mgr.getVariableNames().size();
} Try / catch
try {
return callback.apply(exchange, request);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Failed to extract all URI variables")) {
log.error("Request URI does not satisfy the resource template: " + request.uri(), e);
throw new McpError("Invalid resource URI: " + request.uri());
} throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- Single parameter must be of type ElicitRequest:
- First parameter must be of type Double or double: {method.ge
- Second parameter must be of type String: {method.getName()}
- Third parameter must be of type String: {method.getName()} i
- Method must have parameters for all URI variables. Expected
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/0b4927dbb8b14bfc.
Report an issue: GitHub.