spring-projects/spring-ai · error · IllegalArgumentException
Method must have parameters for all URI variables. Expected
Error message
Method must have parameters for all URI variables. Expected ${expected} URI variable parameters, but found ${found}: ${method} in ${declaringClass}. URI variables: ${uriVariables} What it means
Thrown when the number of method parameters left after removing 'special' parameters (exchange, ReadResourceRequest, @McpProgressToken, McpMeta, request context) does not equal the number of URI variables extracted from the resource URI template. Every URI variable in the @McpResource uri must have exactly one corresponding String parameter.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/AbstractMcpResourceMethodCallback.java:365
throw new IllegalArgumentException("Method cannot have more than one ReadResourceRequest parameter: "
+ method.getName() + " in " + method.getDeclaringClass().getName());
}
// Check if we have more than one meta parameter
if (metaParamCount > 1) {
throw new IllegalArgumentException("Method cannot have more than one McpMeta parameter: " + method.getName()
+ " in " + method.getDeclaringClass().getName());
}
// Calculate how many parameters should be for URI variables
int requestContextParamCount = hasRequestContextParam ? 1 : 0;
int specialParamCount = exchangeParamCount + requestParamCount + progressTokenParamCount + metaParamCount
+ requestContextParamCount;
int uriVarParamCount = parameters.length - specialParamCount;
// Check if we have the right number of parameters for URI variables
if (uriVarParamCount != this.uriVariables.size()) {
throw new IllegalArgumentException(
"Method must have parameters for all URI variables. Expected " + this.uriVariables.size()
+ " URI variable parameters, but found " + uriVarParamCount + ": " + method.getName()
+ " in " + method.getDeclaringClass().getName() + ". URI variables: " + this.uriVariables);
}
// Check that all non-special parameters are String type (for URI variables)
for (Parameter param : parameters) {
// Skip @McpProgressToken annotated parameters
if (param.isAnnotationPresent(McpProgressToken.class)) {
continue;
}
Class<?> paramType = param.getType();
if (!McpSyncRequestContext.class.isAssignableFrom(paramType)
&& !McpAsyncRequestContext.class.isAssignableFrom(paramType) && !isExchangeOrContextType(paramType)
&& !ReadResourceRequest.class.isAssignableFrom(paramType)
&& !McpMeta.class.isAssignableFrom(paramType) && !String.class.isAssignableFrom(paramType)) {
throw new IllegalArgumentException("URI variable parameters must be of type String: " + method.getName()View on GitHub (pinned to 98a7beda4f)
Solutions
- Make the method declare exactly one String parameter per URI variable in the template, in order
- Fix the @McpResource uri template so its variables match the method signature
- Verify the uriVariables supplied to the builder/annotation processing are the ones parsed from the actual URI
Example fix
// before
@McpResource(uri = "db://{table}/{id}")
public String read(String table) { ... }
// after
@McpResource(uri = "db://{table}/{id}")
public String read(String table, String id) { ... } Defensive patterns
Strategy: validation
Validate before calling
List<String> vars = extractUriVariables(uri); // names inside {…}
List<Class<?>> uriParams = Arrays.stream(m.getParameters()).map(Parameter::getType)
.filter(t -> !isSpecialParam(t)).toList();
if (uriParams.size() != vars.size()) throw new IllegalStateException("URI " + uri + " expects " + vars.size() + " params, method has " + uriParams.size()); Try / catch
try {
registerResources(callbacks);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("URI variables")) {
throw new ConfigurationException("Fix @McpResource uri template vs method signature: " + e.getMessage(), e);
}
throw e;
} Prevention
- Change the template and signature together in one commit
- Keep URI variable parameters as plain String in order
- Add a unit test that builds a callback for every @McpResource method at startup
When it happens
Trigger: @McpResource(uri = "db://{table}/{id}") with a method that has only one String parameter (or three), or with an extra non-special parameter that is counted as a URI variable slot; validateParametersWithUriVariables compares uriVarParamCount to this.uriVariables.size().
Common situations: Adding a path segment to the URI without adding a method parameter; typos making a variable unrecognized; passing the wrong uriVariables list into the resource callback builder; changing a method signature without updating the template.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- URI variable parameters must be of type String: ${method} in
- URI must not be null or empty
- Method must have exactly 1 parameter (List<McpSchema.Resourc
- Method must have void or Mono<Void> return type:
- Method must have void return type: " + method.getName() + "
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/48092557c4106142.
Report an issue: GitHub.