spring-projects/spring-ai · error · java.lang.IllegalArgumentException
Unsupported exchange type: {exchange != null ? exchange.getC
Error message
Unsupported exchange type: {exchange != null ? exchange.getClass().getName() : "null"} for Sync method: {method.getName()} in {method.getDeclaringClass().getName()} What it means
When a sync prompt method declares an McpSyncServerExchange parameter, assignExchangeType requires the runtime exchange to actually be a McpSyncServerExchange. Any other (or null) exchange reaches the fallback throw, indicating the callback was invoked from an incompatible server path.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/SyncMcpPromptMethodCallback.java:82
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());
}
}
else if (McpSyncServerExchange.class.isAssignableFrom(paramType)) {
if (exchange instanceof McpSyncServerExchange syncServerExchange) {
return syncServerExchange;
}
throw new IllegalArgumentException(
"Unsupported exchange type: " + (exchange != null ? exchange.getClass().getName() : "null")
+ " 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 exchange and request.
* <p>
* This method builds the arguments for the method call, invokes the method, and
* converts the result to a GetPromptResult.
* @param exchange The server exchange, may be null if the method doesn't require it
* @param request The prompt request, must not be null
* @return The prompt result
* @throws McpError if there is an error invoking the prompt methodView on GitHub (pinned to 98a7beda4f)
Solutions
- Ensure the sync server supplies a McpSyncServerExchange to the callback
- Register the callback only with a sync MCP server
- If the exchange is null, fix the dispatch/transport configuration so a real exchange is passed
Example fix
// before asyncCallback applied on sync path with McpTransportContext // after syncCallback applied on sync server path receiving McpSyncServerExchange
Defensive patterns
Strategy: type-guard
Validate before calling
if (exchange == null || !(exchange instanceof McpSyncServerExchange)) throw new IllegalStateException("expected McpSyncServerExchange"); Type guard
boolean isSyncExchange(Object e) { return e instanceof McpSyncServerExchange; } Try / catch
try { r = callback.apply(exchange, req); } catch (IllegalArgumentException e) { log.error("Bad exchange {} for sync prompt", exchange, e); } Prevention
- Do not call callback.apply() directly outside framework dispatch
- Keep MCP module versions aligned
- Ensure the sync server is the only caller of sync callbacks
When it happens
Trigger: A method expecting McpSyncServerExchange is invoked with a different exchange object (async exchange, transport context, or null).
Common situations: Custom or misconfigured transports calling the callback directly; swapping server implementations without re-registering callbacks; version mismatches between MCP modules.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Unsupported exchange type: {exchange != null ? exchange.getC
- Unsupported exchange type: ${exchangeType} for method: ${met
- Unsupported Sync exchange type: {syncServerExchange.getClass
- Unsupported Async exchange type: {asyncServerExchange.getCla
- Async prompt method must not declare parameter of type: {par
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/2574b62eb3606f76.
Report an issue: GitHub.