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

  1. Change the first parameter of the progress method to Double (or primitive double).
  2. If the value should be integral, convert at the call site and keep the method signature as (Double, String, String).
  3. 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

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


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/2d15087681e32200. Report an issue: GitHub.