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

Method must have either 1 parameter (ProgressNotification)…

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.

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.

Appendix: 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)