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 method: {method.getName()} in {method.getDeclaringClass().getName()} What it means
The fallback branch of assignExchangeType in AsyncStatelessMcpPromptMethodCallback throws when the runtime exchange object is neither McpTransportContext, McpSyncServerExchange, nor McpAsyncServerExchange (or is null). It signals the callback was invoked with an unrecognized exchange type from an unsupported transport path.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AsyncStatelessMcpPromptMethodCallback.java:84
@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, 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 A Mono that emits the prompt result
* @throws McpError if there is an error invoking the prompt method
* @throws IllegalArgumentException if the request is null
*/
@Override
public Mono<GetPromptResult> apply(McpTransportContext context, GetPromptRequest request) {
if (request == null) {View on GitHub (pinned to 98a7beda4f)
Solutions
- Pass a supported exchange type: McpTransportContext, McpSyncServerExchange, or McpAsyncServerExchange
- If the exchange is null, ensure the server supplies a real exchange/transport context
- Upgrade or align library versions so transport and callback layers agree on exchange types
Example fix
// before callback.apply(new MyCustomExchange(), request); // after callback.apply(mcpAsyncServerExchange, request);
Defensive patterns
Strategy: type-guard
Validate before calling
if (exchange == null || !(exchange instanceof McpTransportContext || exchange instanceof McpSyncServerExchange || exchange instanceof McpAsyncServerExchange)) throw new IllegalArgumentException("unsupported exchange"); Type guard
boolean supportedExchange(Object e) { return e instanceof McpTransportContext || e instanceof McpSyncServerExchange || e instanceof McpAsyncServerExchange; } Try / catch
try { ctx = callback.apply(exchange, req); } catch (IllegalArgumentException e) { log.error("Unsupported exchange {}", exchange == null ? "null" : exchange.getClass(), e); } Prevention
- Only invoke callbacks from the MCP framework dispatch, not directly
- Pin consistent versions of mcp core and annotation modules
- Wrap custom transports and map their exchange to McpTransportContext
When it happens
Trigger: Invoking the prompt callback with an exchange argument of an unexpected type (or null) that no branch of assignExchangeType can handle.
Common situations: Custom transport implementations passing a novel exchange object; misuse of the callback API by calling apply/assignExchangeType directly with the wrong argument; version mismatches where new exchange types are not yet handled.
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/e56c54fa13d7c2e4.
Report an issue: GitHub.