spring-projects/spring-ai · error · java.lang.IllegalArgumentException
First parameter must be of type Double or double: {method.ge
Error message
First parameter must be of type Double or double: {method.getName()} in {method.getDeclaringClass().getName()} has parameter of type {parameters[0].getType().getName()} What it means
When an @McpProgress-annotated method declares three parameters, the callback validation requires the first parameter to be a progress value of type Double or double. AbstractMcpProgressMethodCallback.validateParameters throws this IllegalArgumentException when the first parameter has any other type. This ensures the progress ratio (0.0-1.0) can be delivered correctly.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/progress/AbstractMcpProgressMethodCallback.java:112
"Method must have either 1 parameter (ProgressNotification) or 3 parameters (Double, String, String): "
+ method.getName() + " in " + method.getDeclaringClass().getName() + " has "
+ parameters.length + " parameters");
}
// Check parameter types
if (parameters.length == 1) {
// Single parameter must be ProgressNotification
if (!ProgressNotification.class.isAssignableFrom(parameters[0].getType())) {
throw new IllegalArgumentException("Single parameter must be of type ProgressNotification: "
+ method.getName() + " in " + method.getDeclaringClass().getName() + " has parameter of type "
+ parameters[0].getType().getName());
}
}
else {
// Three parameters must be Double, String, String
if (!Double.class.isAssignableFrom(parameters[0].getType())
&& !double.class.isAssignableFrom(parameters[0].getType())) {
throw new IllegalArgumentException("First parameter must be of type Double or double: "
+ method.getName() + " in " + method.getDeclaringClass().getName() + " has parameter of type "
+ parameters[0].getType().getName());
}
if (!String.class.isAssignableFrom(parameters[1].getType())) {
throw new IllegalArgumentException("Second parameter must be of type String: " + method.getName()
+ " in " + method.getDeclaringClass().getName() + " has parameter of type "
+ parameters[1].getType().getName());
}
if (!String.class.isAssignableFrom(parameters[2].getType())) {
throw new IllegalArgumentException("Third parameter must be of type String: " + method.getName()
+ " in " + method.getDeclaringClass().getName() + " has parameter of type "
+ parameters[2].getType().getName());
}
}
}
/**
* Builds the arguments array for invoking the method.View on GitHub (pinned to 98a7beda4f)
Solutions
- Change the first parameter of the progress method to Double (or primitive double).
- If the value should be integral, convert at the call site and keep the method signature as (Double, String, String).
- If you need fewer parameters, use the two-parameter (Double, String) form instead, which skips this three-parameter check.
Example fix
// before
@McpProgress
public void onProgress(int progress, String token, String msg) { ... }
// after
@McpProgress
public void onProgress(Double progress, String token, String msg) { ... } Defensive patterns
Strategy: validation
Validate before calling
if (method.getParameterCount() == 3) {
Class<?> p0 = method.getParameterTypes()[0];
if (!Double.class.equals(p0) && !double.class.equals(p0)) {
throw new IllegalStateException(method + " first param must be Double/double");
}
} Type guard
boolean isValidProgressSignature(Method m) {
return m.getParameterCount() != 3
|| Double.class.isAssignableFrom(m.getParameterTypes()[0]);
} Try / catch
try {
registry.register(callbackBuilder.build());
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("First parameter must be")) {
throw new ConfigurationException("Fix @McpProgress signature: (Double, String[, String])", e);
}
throw e;
} Prevention
- Always copy the documented signature (Double progress, String progressToken) or (Double, String, String).
- Add an architecture/unit test that scans @McpProgress methods and asserts their parameter types.
- Register callbacks in an integration test so validation failures surface before production.
When it happens
Trigger: Declaring a progress handler method with exactly three parameters whose first parameter is not Double/double, e.g. (int progress, String token, String message), then registering it via AsyncMcpProgressMethodCallback or SyncMcpProgressMethodCallback builder; validateMethod runs the check at registration time.
Common situations: Developers guess the progress callback signature instead of following the (Double progress, String progressToken, String message) contract; using Integer or float for the progress value; porting a two-parameter handler to three parameters and keeping the original first-arg type.
Related errors
- Second parameter must be of type String: {method.getName()}
- Third parameter must be of type String: {method.getName()} i
- Single parameter must be of type ElicitRequest:
- Method must have exactly 1 parameter (List<McpSchema.Prompt>
- Parameter must be of type List<McpSchema.Prompt>:
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/2d15087681e32200.
Report an issue: GitHub.