spring-projects/spring-ai · error · IllegalArgumentException

Method cannot have more than one exchange parameter: ${metho

Error message

Method cannot have more than one exchange parameter: ${method} in ${declaringClass}

What it means

Thrown when a resource method declares more than one exchange-type parameter (types matched by isExchangeOrContextType, e.g. McpSyncServerExchange/McpAsyncServerExchange). At most one exchange object is provided per call, so duplicates are ambiguous and rejected at registration.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/AbstractMcpResourceMethodCallback.java:243

							+ method.getName() + " in " + method.getDeclaringClass().getName());
				}
				if (McpPredicates.isNotReactiveReturnType.test(method)) {
					throw new IllegalArgumentException(
							"Async complete methods should use McpAsyncRequestContext instead of McpSyncRequestContext parameter: "
									+ method.getName() + " in " + method.getDeclaringClass().getName());
				}
				hasRequestContextParam = true;
			}
			else if (McpMeta.class.isAssignableFrom(paramType)) {
				if (hasMetaParam) {
					throw new IllegalArgumentException("Method cannot have more than one McpMeta parameter: "
							+ method.getName() + " in " + method.getDeclaringClass().getName());
				}
				hasMetaParam = true;
			}
			else if (isExchangeOrContextType(paramType)) {
				if (hasExchangeParam) {
					throw new IllegalArgumentException("Method cannot have more than one exchange parameter: "
							+ method.getName() + " in " + method.getDeclaringClass().getName());
				}
				hasExchangeParam = true;
			}
			else if (ReadResourceRequest.class.isAssignableFrom(paramType)
					|| String.class.isAssignableFrom(paramType)) {
				if (hasRequestOrUriParam) {
					throw new IllegalArgumentException(
							"Method cannot have more than one ReadResourceRequest or String parameter: "
									+ method.getName() + " in " + method.getDeclaringClass().getName());
				}
				hasRequestOrUriParam = true;
				hasValidParams = true;
			}
			else {
				throw new IllegalArgumentException(
						"Method parameters must be exchange, ReadResourceRequest, String, McpMeta, or @McpProgressToken when no URI variables are present: "
								+ method.getName() + " in " + method.getDeclaringClass().getName()

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Keep a single exchange parameter and drop the duplicate
  2. Prefer the current McpSyncRequestContext/McpAsyncRequestContext style if migrating from raw exchange parameters

Example fix

// before
public String read(McpSyncServerExchange ex1, McpSyncServerExchange ex2) { ... }
// after
public String read(McpSyncServerExchange ex) { ... }
Defensive patterns

Strategy: validation

Validate before calling

long exCount = Arrays.stream(method.getParameterTypes())
    .filter(t -> McpSyncServerExchange.class.isAssignableFrom(t) || McpAsyncServerExchange.class.isAssignableFrom(t))
    .count();
if (exCount > 1) throw new IllegalStateException(method + " declares " + exCount + " exchange parameters");

Try / catch

try { provider.build(...); }
catch (IllegalArgumentException e) {
  if (e.getMessage().contains("more than one exchange parameter")) { /* keep a single exchange param */ }
  else throw e;
}

Prevention

When it happens

Trigger: A @McpResource method (no URI variables) has two parameters of an exchange/context type; triggered when hasExchangeParam is already true and a second exchange-typed parameter is encountered.

Common situations: Copy-pasting an older exchange-based signature alongside a new one; migrating between exchange-style and context-style APIs and keeping both parameters.

Understand the failure class

Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.

Related errors


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