spring-projects/spring-ai · error · IllegalArgumentException

Method parameters must be exchange, CompleteRequest, Complet

Error message

Method parameters must be exchange, CompleteRequest, CompleteArgument, or String: {method} in {class} has parameter of type {paramType}

What it means

Thrown when a @McpComplete method parameter is not one of the allowed types: MCP exchange, McpTransportContext, McpSyncRequestContext/McpAsyncRequestContext, CompleteRequest, CompleteRequest.CompleteArgument, or String. Any other parameter type makes the method signature invalid for completion callbacks.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/complete/AbstractMcpCompleteMethodCallback.java:254

				}
				hasExchangeParam = true;
			}
			else if (CompleteRequest.class.isAssignableFrom(paramType)) {
				if (hasRequestParam) {
					throw new IllegalArgumentException("Method cannot have more than one CompleteRequest parameter: "
							+ method.getName() + " in " + method.getDeclaringClass().getName());
				}
				hasRequestParam = true;
			}
			else if (CompleteRequest.CompleteArgument.class.isAssignableFrom(paramType)) {
				if (hasArgumentParam) {
					throw new IllegalArgumentException("Method cannot have more than one CompleteArgument parameter: "
							+ method.getName() + " in " + method.getDeclaringClass().getName());
				}
				hasArgumentParam = true;
			}
			else if (!String.class.isAssignableFrom(paramType)) {
				throw new IllegalArgumentException(
						"Method parameters must be exchange, CompleteRequest, CompleteArgument, or String: "
								+ method.getName() + " in " + method.getDeclaringClass().getName()
								+ " has parameter of type " + paramType.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, request, argument).
	 * @param method The method to build arguments for
	 * @param exchangeOrContext The server exchange or transport context
	 * @param request The complete request
	 * @return An array of arguments for the method invocation
	 */
	protected Object[] buildArgs(Method method, Object exchangeOrContext, CompleteRequest request) {

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Change unsupported parameters to allowed types (String for the value, plus at most one each of exchange/context/CompleteRequest/CompleteArgument)
  2. Remove extra parameters entirely if not needed
  3. Use @McpProgressToken or McpMeta only if that data is needed, as those are exempt from this check

Example fix

// before
@McpComplete(prompt="code")
public String complete(Integer index, CompleteRequest req) { ... }
// after
@McpComplete(prompt="code")
public String complete(CompleteRequest.CompleteArgument argument) { ... }
Defensive patterns

Strategy: validation

Validate before calling

Set<Class<?>> allowed = Set.of(String.class, McpTransportContext.class, McpSyncRequestContext.class, McpAsyncRequestContext.class, CompleteRequest.class, CompleteRequest.CompleteArgument.class);
for (Class<?> t : m.getParameterTypes()) {
  boolean ok = allowed.stream().anyMatch(a -> a.isAssignableFrom(t)) || t.isAnnotationPresent(McpProgressToken.class) || McpMeta.class.isAssignableFrom(t);
  if (!ok) throw new IllegalStateException("Unsupported completion parameter type " + t.getName() + " in " + m);
}

Type guard

boolean isAllowedCompletionParam(Class<?> t) {
  return String.class.isAssignableFrom(t) || McpTransportContext.class.isAssignableFrom(t)
    || McpSyncRequestContext.class.isAssignableFrom(t) || McpAsyncRequestContext.class.isAssignableFrom(t)
    || CompleteRequest.class.isAssignableFrom(t) || CompleteRequest.CompleteArgument.class.isAssignableFrom(t);
}

Try / catch

try {
    callbackBuilder.build();
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Method parameters must be")) { /* fix or remove the offending parameter type */ }
    throw e;
}

Prevention

When it happens

Trigger: Adding an arbitrary typed parameter (e.g. Integer, custom DTO, Map) to a @McpComplete method; forgetting that only String is accepted for the value being completed.

Common situations: Using tool-style signatures (custom input DTOs) in a completion method; IDE-generated signatures with wrong types; migrating from @McpTool annotations where richer parameter types are allowed.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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