spring-projects/spring-ai · error · java.lang.IllegalArgumentException
Method must return a Mono<T> where T is one of GetPromptResu
Error message
Method must return a Mono<T> where T is one of GetPromptResult, List<PromptMessage>, List<String>, PromptMessage, or String: {method.getName()} in {method.getDeclaringClass().getName()} returns {returnType.getName()} What it means
AsyncMcpPromptMethodCallback validates that any @McpPrompt-annotated method handled by the async callback returns a Mono<T>, since async MCP prompt execution is reactive. If the method returns a plain (non-Mono) type, the callback cannot wrap it in the reactive pipeline and throws IllegalArgumentException at registration/validation time.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AsyncMcpPromptMethodCallback.java:157
.data(ErrorUtils.findCauseUsingPlainJava(e).getMessage())
.build());
}
});
}
@Override
protected boolean isSupportedExchangeOrContextType(Class<?> paramType) {
return (McpAsyncServerExchange.class.isAssignableFrom(paramType)
|| McpTransportContext.class.isAssignableFrom(paramType));
}
@Override
protected void validateReturnType(Method method) {
Class<?> returnType = method.getReturnType();
// For AsyncMcpPromptMethodCallback, the method must return a Mono
if (!Mono.class.isAssignableFrom(returnType)) {
throw new IllegalArgumentException(
"Method must return a Mono<T> where T is one of GetPromptResult, List<PromptMessage>, "
+ "List<String>, PromptMessage, or String: " + 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 AsyncMcpPromptMethodCallback instances.
* <p>
* This builder provides a fluent API for constructing AsyncMcpPromptMethodCallbackView on GitHub (pinned to 98a7beda4f)
Solutions
- Change the method return type to Mono<T> where T is GetPromptResult, List<PromptMessage>, List<String>, PromptMessage, or String
- Wrap the existing return value: return Mono.just(result)
- If the method is inherently synchronous, register it with the sync MCP server / SyncMcpPromptMethodCallback instead
Example fix
// before
@McpPrompt(description = "greet")
public String greet(GreetRequest req) { return "hi"; }
// after
@McpPrompt(description = "greet")
public Mono<String> greet(GreetRequest req) { return Mono.just("hi"); } Defensive patterns
Strategy: validation
Validate before calling
if (!Mono.class.isAssignableFrom(method.getReturnType())) { throw new IllegalStateException(method + " must return Mono<...>"); } Type guard
boolean returnsMono(Method m) { return Mono.class.isAssignableFrom(m.getReturnType()); } Try / catch
try { server.addPrompt(callback); } catch (IllegalArgumentException e) { /* fix signature, not retry */ } Prevention
- Use Mono returns for all @McpPrompt methods in reactive servers
- Add a startup-time reflection test asserting Mono return types
- Keep sync and async prompt beans in separate packages/configurations
When it happens
Trigger: Registering a class with an @McpPrompt method whose return type is GetPromptResult, List<PromptMessage>, String, etc. directly (not Mono<...>) while using AsyncMcpPromptMethodCallback (async MCP server).
Common situations: Reusing a sync-style prompt bean on an async MCP server; copying examples from sync MCP docs into a reactive setup; forgetting to wrap the result with Mono.just().
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
- Expected Mono<Void> but got Mono<
- Method must have void or Mono<Void> return type:
- Method must have void or Mono<Void> return type:
- Method must return Mono<ElicitResult> or Mono<StructuredElic
- Expected Mono<Void> but got Mono<
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/c0542dbadcbf60f6.
Report an issue: GitHub.