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

Third parameter must be of type String: {method.getName()} i

Error message

Third parameter must be of type String: {method.getName()} in {method.getDeclaringClass().getName()} has parameter of type {parameters[2].getType().getName()}

What it means

For three-parameter progress methods, the third parameter must be a String (the human-readable progress message). validateParameters throws this IllegalArgumentException when parameters[2] is not String-assignable. The message lets clients display progress detail text.

Source

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

						+ 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.
	 * <p>
	 * This method constructs an array of arguments based on the method's parameter types
	 * and the available values (exchange, notification).
	 * @param method The method to build arguments for
	 * @param exchange The server exchange
	 * @param notification The progress notification
	 * @return An array of arguments for the method invocation
	 */
	protected Object[] buildArgs(Method method, Object exchange, ProgressNotification notification) {
		Parameter[] parameters = method.getParameters();

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Change the third parameter to java.lang.String.
  2. If structured data is needed, encode it into the String message (e.g. JSON) and parse inside the method.
  3. Drop the third parameter and use the two-parameter (Double, String) signature if no message is needed.

Example fix

// before
public void onProgress(Double progress, String token, int percent) { ... }
// after
public void onProgress(Double progress, String token, String message) { ... }
Defensive patterns

Strategy: validation

Validate before calling

if (method.getParameterCount() == 3
        && !String.class.equals(method.getParameterTypes()[2])) {
    throw new IllegalStateException(method + " third param must be String (message)");
}

Type guard

boolean hasStringMessage(Method m) {
    return m.getParameterCount() != 3
        || String.class.isAssignableFrom(m.getParameterTypes()[2]);
}

Try / catch

try {
    builder.method(method).bean(bean).build();
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Third parameter must be")) {
        throw new ConfigurationException("Third param of progress method must be String", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Registering a progress callback with a third parameter of a non-String type, e.g. (Double progress, String token, Integer total) or (Double, String, Object detail).

Common situations: Trying to receive structured progress data (counts, totals, JSON payloads) in the third slot instead of a String message; mismatched signature after adding a parameter to an existing two-arg handler.

Related errors


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