spring-projects/spring-ai · error · IllegalArgumentException

Method parameters must be exchange, ReadResourceRequest…

Error message

Method parameters must be exchange, ReadResourceRequest, String, McpMeta, or @McpProgressToken when no URI variables are present: ${method} in ${declaringClass} has parameter of type ${paramType}

What it means

Thrown when a resource method without URI variables has a parameter whose type is none of the supported ones: exchange types, ReadResourceRequest, String, McpMeta, or a parameter annotated @McpProgressToken. The framework cannot bind arbitrary types for URI-variable-less resource methods, so the method is rejected.

Solutions

  1. Remove or retype the unsupported parameter to ReadResourceRequest or String
  2. If the parameter was meant to bind a URI variable, add {var} placeholders to the @McpResource uri and validateParametersWithUriVariables will apply instead
  3. Annotate progress-related parameters with @McpProgressToken so they are skipped by validation

Example fix

// before
@McpResource(uri = "db://rows")
public String read(Map<String, String> filters) { ... }
// after
@McpResource(uri = "db://rows")
public String read(ReadResourceRequest request) { ... }
Defensive patterns

Strategy: validation

Validate before calling

Set<Class<?>> allowed = Set.of(ReadResourceRequest.class, String.class, McpMeta.class,
    McpSyncRequestContext.class, McpAsyncRequestContext.class);
for (Parameter p : method.getParameters()) {
  if (p.isAnnotationPresent(McpProgressToken.class)) continue;
  Class<?> t = p.getType();
  if (!allowed.stream().anyMatch(a -> a.isAssignableFrom(t))) {
    throw new IllegalStateException("Unsupported param " + t + " in " + method);
  }
}

Type guard

static boolean isSupportedResourceParam(Parameter p) {
  Class<?> t = p.getType();
  return p.isAnnotationPresent(McpProgressToken.class)
      || McpMeta.class.isAssignableFrom(t)
      || ReadResourceRequest.class.isAssignableFrom(t)
      || String.class.isAssignableFrom(t);
}

Try / catch

try { provider.build(...); }
catch (IllegalArgumentException e) {
  if (e.getMessage().contains("parameters must be exchange, ReadResourceRequest, String, McpMeta")) { /* retype or remove the offending param */ }
  else throw e;
}

Prevention

When it happens

Trigger: A @McpResource URI contains no {var} placeholders but the handler method includes a parameter of an unsupported type (e.g. Map, custom POJO, Integer) that is not annotated with @McpProgressToken.

Common situations: Reusing a tool-style handler signature for a resource; forgetting that URI variables are only allowed when the URI template has placeholders; passing a custom request object instead of ReadResourceRequest.

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/ef1de0c4caa0022e. Report an issue: GitHub.

Appendix: source

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

			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()
								+ " has parameter of type " + paramType.getName());
			}
		}

		if (!hasValidParams && nonSpecialParamCount > 0) {
			throw new IllegalArgumentException(
					"Method must have either ReadResourceRequest or String parameter when no URI variables are present: "
							+ method.getName() + " in " + method.getDeclaringClass().getName());
		}
	}

	protected void validateParamType(Class<?> paramType) {
	}

	/**
	 * Validates method parameters when URI variables are present. This method provides

View on GitHub (pinned to 98a7beda4f)