spring-projects/spring-ai · error · IllegalArgumentException
Unsupported Async exchange type: ${exchangeType} for Sync me
Error message
Unsupported Async exchange type: ${exchangeType} for Sync method: ${method} in ${declaringClass} What it means
The sync stateless prompt callback received an McpAsyncServerExchange where it expected a sync (or context-less) exchange. Sync callbacks can only work with McpSyncServerExchange; an async exchange is incompatible with a synchronous method invocation, so assignExchangeType throws IllegalArgumentException naming the offending exchange type.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/SyncStatelessMcpPromptMethodCallback.java:75
throw new IllegalArgumentException(
"Stateless Streamable-Http prompt method must not declare parameter of type: " + paramType.getName()
+ ". Use McpTransportContext 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) {
return syncServerExchange.transportContext();
}
else if (exchange instanceof McpAsyncServerExchange asyncServerExchange) {
throw new IllegalArgumentException("Unsupported Async exchange type: "
+ asyncServerExchange.getClass().getName() + " for Sync method: " + method.getName() + " in "
+ method.getDeclaringClass().getName());
}
}
throw new IllegalArgumentException(
"Unsupported exchange type: " + (exchange != null ? exchange.getClass().getName() : "null")
+ " for method: " + method.getName() + " in " + method.getDeclaringClass().getName());
}
/**
* Apply the callback to the given context and request.
* <p>
* This method builds the arguments for the method call, invokes the method, and
* converts the result to a GetPromptResult.
* @param context The transport context, may be null if the method doesn't require it
* @param request The prompt request, must not be null
* @return The prompt resultView on GitHub (pinned to 98a7beda4f)
Solutions
- Register the annotated methods with a sync server (McpSyncServer) so sync callbacks receive McpSyncServerExchange.
- Remove mixed async server wiring that produces McpAsyncServerExchange for the same methods.
- If the method must stay sync, ensure the exchange passed down is sync or null-in-stateless mode per framework conventions.
Example fix
// before (async exchange routed to sync callback) McpAsyncServerExchange asyncExchange = ...; callback.assignExchangeType(asyncExchange); // after (sync exchange) McpSyncServerExchange syncExchange = ...; callback.assignExchangeType(syncExchange);
Defensive patterns
Strategy: type-guard
Validate before calling
if (exchange instanceof McpAsyncServerExchange) {
throw new IllegalStateException("Async exchange routed to sync callback - fix server wiring");
} Type guard
static boolean isSyncExchange(Object exchange) {
return exchange instanceof McpSyncServerExchange || exchange == null;
} Try / catch
try {
callback.assignExchangeType(exchange);
} catch (IllegalArgumentException e) {
throw new IllegalStateException("Sync/async exchange mismatch in MCP wiring", e);
} Prevention
- Register annotated methods with the matching server flavor (McpSyncServer for sync callbacks).
- Avoid mixing async and sync server instances in the same application context.
- Verify wiring with integration tests before startup.
When it happens
Trigger: Invoking a sync stateless prompt callback while passing an exchange object of type McpAsyncServerExchange; typically a wiring/configuration mistake where an async exchange is routed to a sync callback handler.
Common situations: Mixing async and sync annotated handler registrations in one application; custom code calling the callback's apply/assignExchangeType path with an exchange obtained from an async server instance.
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
- Unsupported Async exchange type: {syncServerExchange.getClas
- Unsupported Sync exchange type: {syncServerExchange.getClass
- Unsupported Async exchange type: {asyncServerExchange.getCla
- Unsupported exchange type: ${exchangeType} for method: ${met
- Sync complete methods should use McpSyncRequestContext inste
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/e1d1374249fc79d6.
Report an issue: GitHub.