spring-projects/spring-ai · error · java.lang.IllegalArgumentException
Method must return either GetPromptResult, List
Error message
Method must return either GetPromptResult, List<PromptMessage>, List<String>, PromptMessage, String, or Mono<T>: {method.getName()} in {method.getDeclaringClass().getName()} returns {returnType.getName()} What it means
AsyncStatelessMcpPromptMethodCallback validates that the prompt method returns one of GetPromptResult, List (e.g. List<PromptMessage> or List<String>), PromptMessage, String, or a Mono of any of these. Any other return type is rejected with IllegalArgumentException because the callback cannot convert it into a prompt response.
Solutions
- Change the return type to one of GetPromptResult, List<PromptMessage>, List<String>, PromptMessage, String, or Mono<T> of those
- Map your custom result into a GetPromptResult or PromptMessage before returning
- Convert CompletableFuture to Mono using Mono.fromFuture(...)
Example fix
// before
@McpPrompt(description = "p")
public MyResult p(Request req) {...}
// after
@McpPrompt(description = "p")
public Mono<GetPromptResult> p(Request req) { return Mono.just(new GetPromptResult(description)); } Defensive patterns
Strategy: validation
Validate before calling
Set<Class<?>> ok = Set.of(GetPromptResult.class, List.class, PromptMessage.class, String.class, Mono.class); if (ok.stream().noneMatch(c -> c.isAssignableFrom(method.getReturnType()))) throw new IllegalStateException("bad return type"); Try / catch
try { server.addPrompt(cb); } catch (IllegalArgumentException e) { /* fix return type */ } Prevention
- Return GetPromptResult/PromptMessage/String or their Mono wrappers only
- Map domain objects to GetPromptResult before returning
- Use Mono.fromFuture instead of CompletableFuture returns
When it happens
Trigger: Registering an @McpPrompt method whose return type is a POJO, Map, CompletableFuture, or other non-supported type while using the async stateless callback.
Common situations: Returning custom domain objects from prompt methods; forgetting to map a domain object to GetPromptResult/PromptMessage; using CompletableFuture instead of Mono.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Method must return a Mono
- ASYNC Providers don't support imperative (non-reactive)…
- Asynchronous progress methods must return void or Mono
- Expected Mono but got Mono<
- Expected Mono but got Mono<
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/7e2c1a7e5bbf6ab1.
Report an issue: GitHub.
Appendix: source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AsyncStatelessMcpPromptMethodCallback.java:156
}
});
}
@Override
protected boolean isSupportedExchangeOrContextType(Class<?> paramType) {
return McpTransportContext.class.isAssignableFrom(paramType);
}
@Override
protected void validateReturnType(Method method) {
Class<?> returnType = method.getReturnType();
boolean validReturnType = GetPromptResult.class.isAssignableFrom(returnType)
|| List.class.isAssignableFrom(returnType) || PromptMessage.class.isAssignableFrom(returnType)
|| String.class.isAssignableFrom(returnType) || Mono.class.isAssignableFrom(returnType);
if (!validReturnType) {
throw new IllegalArgumentException("Method must return either GetPromptResult, List<PromptMessage>, "
+ "List<String>, PromptMessage, String, or Mono<T>: " + method.getName() + " in "
+ method.getDeclaringClass().getName() + " returns " + returnType.getName());
}
}
/**
* Create a new builder.
* @return A new builder instance
*/
public static Builder builder() {
return new Builder();
}
/**
* Builder for creating AsyncStatelessMcpPromptMethodCallback instances.
* <p>
* This builder provides a fluent API for constructing
* AsyncStatelessMcpPromptMethodCallback instances with the required parameters.View on GitHub (pinned to 98a7beda4f)