spring-projects/spring-ai · error · java.lang.IllegalArgumentException
Async prompt method must not declare parameter of type: {par
Error message
Async prompt method must not declare parameter of type: {paramType.getName()}. Use McpAsyncServerExchange instead. Method: {this.method.getName()} in {this.method.getDeclaringClass().getName()} What it means
AsyncMcpPromptMethodCallback.validateParamType rejects McpSyncServerExchange parameters in methods bound to an async (reactive) prompt callback. A synchronous exchange cannot be provided to an async handler, so registration fails immediately with this IllegalArgumentException, directing you to use McpAsyncServerExchange instead.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AsyncMcpPromptMethodCallback.java:55
*
* This class provides a way to convert methods annotated with {@link McpPrompt} into
* callback functions that can be used to handle prompt requests asynchronously. It
* supports various method signatures and return types.
*
* @author Christian Tzolov
*/
public final class AsyncMcpPromptMethodCallback extends AbstractMcpPromptMethodCallback
implements BiFunction<McpAsyncServerExchange, GetPromptRequest, Mono<GetPromptResult>> {
private AsyncMcpPromptMethodCallback(Builder builder) {
super(builder.method, builder.bean, builder.prompt);
}
@Override
protected void validateParamType(Class<?> paramType) {
if (McpSyncServerExchange.class.isAssignableFrom(paramType)) {
throw new IllegalArgumentException("Async prompt method must not declare parameter of type: "
+ paramType.getName() + ". Use McpAsyncServerExchange instead." + " Method: "
+ this.method.getName() + " in " + this.method.getDeclaringClass().getName());
}
}
@Override
protected Object assignExchangeType(Class<?> paramType, Object exchange) {
if (McpTransportContext.class.isAssignableFrom(paramType)) {
if (exchange instanceof McpTransportContext transportContext) {
return transportContext;
}
else if (exchange instanceof McpSyncServerExchange syncServerExchange) {
throw new IllegalArgumentException("Unsupported Async exchange type: "
+ syncServerExchange.getClass().getName() + " for Async method: " + method.getName() + " in "
+ method.getDeclaringClass().getName());
}View on GitHub (pinned to 98a7beda4f)
Solutions
- Replace McpSyncServerExchange with McpAsyncServerExchange in the async method signature.
- If the method truly needs synchronous semantics, register it with the sync callback/server variant (McpSyncServer) instead.
- Use McpTransportContext if you only need request metadata/transport context, not the full exchange.
Example fix
// before
@McpPrompt(name = "explain")
public Mono<String> explain(McpSyncServerExchange exchange, @McpArg String topic) { ... }
// after
@McpPrompt(name = "explain")
public Mono<String> explain(McpAsyncServerExchange exchange, @McpArg String topic) { ... } Defensive patterns
Strategy: validation
Validate before calling
for (Parameter p : method.getParameters()) {
if (isAsyncHandler && McpSyncServerExchange.class.isAssignableFrom(p.getType())) {
throw new IllegalStateException("Async method uses McpSyncServerExchange: " + method);
}
} Prevention
- Pair async methods (Mono/Flux returns) with McpAsyncServerExchange, sync methods with McpSyncServerExchange.
- Keep separate bean sets for sync and async servers instead of sharing handler classes.
- Validate handler signatures in a context-loading test.
When it happens
Trigger: Registering an @McpPrompt method on an async server (or processed by the async callback) with signature `myPrompt(McpSyncServerExchange exchange, ...)`. validateParameters calls validateParamType for every parameter and throws on the sync exchange type.
Common situations: Copying a sync handler to an async server without changing the exchange type; migrating a server from sync to async transport; shared utility prompt classes used by both sync and async servers.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Unsupported Async exchange type: {syncServerExchange.getClas
- Unsupported exchange type: {exchange != null ? exchange.getC
- Unsupported exchange type: {exchange != null ? exchange.getC
- Async prompt method must not declare parameter of type: ${pa
- Method must have exactly 1 parameter (List<McpSchema.Prompt>
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/be142a36abf2f0ad.
Report an issue: GitHub.