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, falseView on GitHub (pinned to 98a7beda4f)
Solutions
- 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
- Replace Flux<String> with Mono<List<String>> (collectList())
- If returning CompletableFuture, switch to Mono.fromFuture(...)
- 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
- Validate return types in an application-startup test that registers every @McpComplete bean
- Use only the documented return types: CompleteResult, CompleteCompletion, List<String>, String, Mono<T>
- Remember Flux is not accepted — use Mono<List<T>> via collectList()
- Keep @McpComplete method signatures simple; do complex mapping in helper methods
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
- Either prompt or uri must be provided!
- Method must have exactly 1 parameter (List<McpSchema.Resourc
- Method must have void or Mono<Void> return type:
- Method must have void return type: " + method.getName() + "
- clients must not be empty
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/05464081ac0164ed.
Report an issue: GitHub.