spring-projects/spring-ai · error · IllegalArgumentException

Method must return either CompleteResult, CompleteCompletion

Error message

Method must return either CompleteResult, CompleteCompletion, List<String>, String, or Mono<T>: {method} in {class} returns {returnType}

What it means

At callback construction, validateReturnType checks the annotated method's declared return type against the supported set: CompleteResult, CompleteCompletion, List, String, or Mono. Any other declared return type fails fast with this IllegalArgumentException naming the method, class, and offending return type. (Note: List is accepted without checking its element type here, so bad element types surface later at runtime.)

Source

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

		throw new IllegalArgumentException("Unsupported return type: " + result.getClass().getName());
	}

	/**
	 * Validates that the method return type is compatible with the complete callback.
	 * @param method The method to validate
	 * @throws IllegalArgumentException if the return type is not compatible
	 */
	@Override
	protected void validateReturnType(Method method) {
		Class<?> returnType = method.getReturnType();

		boolean validReturnType = CompleteResult.class.isAssignableFrom(returnType)
				|| CompleteCompletion.class.isAssignableFrom(returnType) || List.class.isAssignableFrom(returnType)
				|| String.class.isAssignableFrom(returnType) || Mono.class.isAssignableFrom(returnType);

		if (!validReturnType) {
			throw new IllegalArgumentException(
					"Method must return either CompleteResult, CompleteCompletion, List<String>, "
							+ "String, or Mono<T>: " + method.getName() + " in " + method.getDeclaringClass().getName()
							+ " returns " + returnType.getName());
		}
	}

	@Override
	protected McpTransportContext resolveTransportContext(Object context) {
		if (context instanceof McpTransportContext c) {
			return c;
		}
		return null;
	}

	/**
	 * Checks if a parameter type is compatible with the exchange type.
	 * @param paramType The parameter type to check
	 * @return true if the parameter type is compatible with the exchange type, false

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Change the method's declared return type to one of: CompleteResult, CompleteCompletion, List<String>, String, or Mono<T> where T is one of the convertible types
  2. Replace Flux<String> with Mono<List<String>> (collectList())
  3. If returning CompletableFuture, switch to Mono.fromFuture(...)
  4. Check the method, class, and returnType names in the message to locate the offending signature

Example fix

// before
public Flux<String> complete(String arg) { ... }
// after
public Mono<List<String>> complete(String arg) {
    return fluxSuggestions(arg).collectList();
}
Defensive patterns

Strategy: validation

Validate before calling

Class<?> rt = method.getReturnType();
boolean ok = CompleteResult.class.isAssignableFrom(rt) || CompleteCompletion.class.isAssignableFrom(rt)
    || List.class.isAssignableFrom(rt) || String.class.isAssignableFrom(rt)
    || Mono.class.isAssignableFrom(rt);
if (!ok) throw new IllegalArgumentException(method + " has unsupported return type " + rt);

Try / catch

try {
    registerCompletion(bean, method);
} catch (IllegalArgumentException e) {
    log.error("Completion method rejected at registration: {}", e.getMessage());
    throw e; // fail fast at startup, not at request time
}

Prevention

When it happens

Trigger: Registering an @McpComplete method whose declared return type is, e.g., Map<String,Object>, CompleteCompletion[] (array), Flux<String>, void, or a custom DTO class.

Common situations: Using Flux instead of Mono (the wrong reactive type); returning a CompletableFuture; returning void from a completion handler; a refactoring that changed the return type without updating the annotation contract.

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