spring-projects/spring-ai · error · IllegalArgumentException

Method must have either ReadResourceRequest or String…

Error message

Method must have either ReadResourceRequest or String parameter when no URI variables are present: ${method} in ${declaringClass}

What it means

Thrown when a URI-variable-less resource method has non-special parameters (excluding @McpProgressToken and McpMeta) but none of them is a ReadResourceRequest or String. The framework requires the resource payload (the read request or URI string) to be bindable, so such a signature is rejected.

Solutions

  1. Add a ReadResourceRequest parameter to receive the resource read request
  2. Or add a String parameter to receive the URI
  3. If the method needs no request data at all, ensure non-special parameters are removed so validation passes

Example fix

// before
@McpResource(uri = "cfg://app")
public String read(McpSyncRequestContext ctx) { ... }
// after
@McpResource(uri = "cfg://app")
public String read(ReadResourceRequest request, McpSyncRequestContext ctx) { ... }
Defensive patterns

Strategy: validation

Validate before calling

boolean hasPayload = Arrays.stream(method.getParameterTypes())
    .anyMatch(t -> ReadResourceRequest.class.isAssignableFrom(t) || String.class.isAssignableFrom(t));
if (!hasPayload) throw new IllegalStateException(method + " lacks a ReadResourceRequest/String parameter");

Try / catch

try { provider.build(...); }
catch (IllegalArgumentException e) {
  if (e.getMessage().contains("must have either ReadResourceRequest or String parameter")) { /* add the missing request param */ }
  else throw e;
}

Prevention

When it happens

Trigger: A @McpResource method with no URI variables whose parameters are only context/exchange/McpMeta types (hasValidParams stays false while nonSpecialParamCount > 0).

Common situations: Writing a resource handler that takes only a context, forgetting the request/URI parameter; deleting the String parameter during a refactor while keeping others.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

					|| 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
	 * 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 validateParametersWithUriVariables(Method method) {
		Parameter[] parameters = method.getParameters();

		// Count special parameters (exchange, request, progress token, and meta)

View on GitHub (pinned to 98a7beda4f)