spring-projects/spring-ai · error · java.lang.IllegalArgumentException

Method must have either 1 parameter (ProgressNotification) o

Error message

Method must have either 1 parameter (ProgressNotification) or 3 parameters (Double, String, String): {method.getName()} in {method.getDeclaringClass().getName()} has {parameters.length} parameters

What it means

Progress handler methods must take exactly one parameter (a ProgressNotification) or exactly three parameters (Double progress, String total/progressToken, String message). Any other parameter count is rejected at registration with this IllegalArgumentException. Note the message template uses literal {…} placeholders that are not interpolated — the actual counts are appended after the colon.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/progress/AbstractMcpProgressMethodCallback.java:93

	 * This method should be implemented by subclasses to handle specific return type
	 * validation.
	 * @param method The method to validate
	 * @throws IllegalArgumentException if the return type is not compatible
	 */
	protected abstract void validateReturnType(Method method);

	/**
	 * Validates method parameters. This method provides common validation logic and
	 * delegates exchange type checking to subclasses.
	 * @param method The method to validate
	 * @throws IllegalArgumentException if the parameters are not compatible
	 */
	protected void validateParameters(Method method) {
		Parameter[] parameters = method.getParameters();

		// Check parameter count - must have either 1 or 3 parameters
		if (parameters.length != 1 && parameters.length != 3) {
			throw new IllegalArgumentException(
					"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())) {

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Reduce or expand the signature to exactly (ProgressNotification) or (Double, String, String).
  2. If you need extra context, capture it via constructor/field injection into the bean instead of method parameters.
  3. Check the parameter order for the three-arg form matches (progress, total, progressToken/message) per the class docs.
  4. Read the appended text after the colon in the message to see the actual offending parameter count.

Example fix

// before
public void onProgress(ProgressNotification n, McpSession session) { ... }
// after
public void onProgress(ProgressNotification n) { ... } // session held as a bean field
Defensive patterns

Strategy: validation

Validate before calling

int n = handlerMethod.getParameterCount();
if (n != 1 && n != 3) {
    throw new IllegalStateException("Progress handler must have 1 (ProgressNotification) or 3 (Double,String,String) params: " + handlerMethod);
}

Try / catch

try {
    registry.registerProgress(bean, method);
} catch (IllegalArgumentException e) {
    logger.error("Invalid progress handler arity: {}", e.getMessage());
}

Prevention

When it happens

Trigger: Annotating a progress handler with 0, 2, 4+ parameters (e.g. onProgress(ProgressNotification n, String extra)) and registering it; constructor-time validateMethod -> validateParameters throws.

Common situations: Adding a context or session parameter to a handler signature; migrating from a three-arg style to the notification-object style but keeping a leftover second parameter; copy-paste from other MCP callback types with different arity rules.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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