spring-projects/spring-ai · error · java.lang.IllegalArgumentException
Unsupported Sync exchange type: {syncServerExchange.getClass
Error message
Unsupported Sync exchange type: {syncServerExchange.getClass().getName()} for Sync method: {method.getName()} in {method.getDeclaringClass().getName()} What it means
When an async stateless prompt method declares an McpTransportContext parameter, assignExchangeType converts the runtime exchange object into that context. A McpSyncServerExchange arriving at an async (stateless) method is a Sync exchange, which is not convertible, so IllegalArgumentException is thrown indicating the mismatch.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AsyncStatelessMcpPromptMethodCallback.java:74
if (McpSyncServerExchange.class.isAssignableFrom(paramType)
|| McpAsyncServerExchange.class.isAssignableFrom(paramType)) {
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) {
throw new IllegalArgumentException("Unsupported Sync exchange type: "
+ syncServerExchange.getClass().getName() + " for Sync method: " + method.getName() + " in "
+ method.getDeclaringClass().getName());
}
else if (exchange instanceof McpAsyncServerExchange asyncServerExchange) {
return asyncServerExchange.transportContext();
}
}
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, andView on GitHub (pinned to 98a7beda4f)
Solutions
- Register the annotated bean with the async MCP server so it receives async exchanges/transport contexts
- Use separate beans (or separate callbacks) for sync and async servers
- Verify the server type (McpAsyncServer vs McpSyncServer) matches the callback type
Example fix
// before McpServer.sync(mcpTransportProvider).prompts(...).build(); // with async stateless callback // after McpServer.async(mcpStreamableServerTransportProvider).prompts(...).build();
Defensive patterns
Strategy: validation
Validate before calling
assert server instanceof McpAsyncServer : "Async stateless callbacks require an async server";
Type guard
boolean compatible(Object exchange) { return exchange instanceof McpTransportContext || exchange instanceof McpAsyncServerExchange; } Try / catch
try { result = callback.apply(exchange, request); } catch (IllegalArgumentException e) { throw new McpError("Exchange/server mismatch: " + e.getMessage()); } Prevention
- Never share one annotated bean between sync and async servers
- Match McpServer.sync/async to the callback class
- Add integration tests exercising each registered prompt end-to-end
When it happens
Trigger: A method declaring McpTransportContext is invoked, but the callback receives a McpSyncServerExchange as the exchange argument — e.g. the method callback is wired into a sync server dispatch path.
Common situations: Mixing sync and async MCP servers with the same annotated bean; registering an async stateless callback on a sync server transport.
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: {asyncServerExchange.getCla
- Unsupported Async exchange type: {syncServerExchange.getClas
- Unsupported exchange type: {exchange != null ? exchange.getC
- Sync prompt method must not declare parameter of type: {para
- Unsupported exchange type: {exchange != null ? exchange.getC
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/82089f5f92e1aae4.
Report an issue: GitHub.