spring-projects/spring-ai · error · IllegalArgumentException
Unsupported exchange type: ${exchangeType} for Async method:
Error message
Unsupported exchange type: ${exchangeType} for Async method: ${method} in ${declaringClass} What it means
Thrown by AsyncMcpResourceMethodCallback.assignExchangeType when the async method declares a McpAsyncServerExchange parameter but the exchange supplied at call time is not one (it is some other type, or null). The async callback can only bind an McpAsyncServerExchange to that parameter, so it throws.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/AsyncMcpResourceMethodCallback.java:92
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());
}
else if (exchange instanceof McpAsyncServerExchange asyncServerExchange) {
return asyncServerExchange.transportContext();
}
}
else if (McpAsyncServerExchange.class.isAssignableFrom(paramType)) {
if (exchange instanceof McpAsyncServerExchange asyncServerExchange) {
return asyncServerExchange;
}
throw new IllegalArgumentException(
"Unsupported exchange type: " + (exchange != null ? exchange.getClass().getName() : "null")
+ " for Async 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 extracts URI variable values from the request URI, builds the arguments
* for the method call, invokes the method, and converts the result to a
* ReadResourceResult.
* @param exchange The server exchange, may be null if the method doesn't require it
* @param request The resource request, must not be null
* @return A Mono that emits the resource resultView on GitHub (pinned to 98a7beda4f)
Solutions
- Pass an McpAsyncServerExchange when invoking the callback (build it from the async server session).
- If you only have a transport context, change the method parameter to McpTransportContext.
- Fix null exchanges by ensuring the dispatcher always resolves a session before calling the annotated method.
Example fix
// before callback.call(syncExchange, request); // wrong exchange type // after McpAsyncServerExchange ex = new McpAsyncServerExchange(session, transportContext); callback.call(ex, request);
Defensive patterns
Strategy: type-guard
Validate before calling
if (exchange == null) throw new IllegalArgumentException("exchange required for @McpAsyncServerExchange methods"); Type guard
static boolean canInvokeAsync(Object exchange) { return exchange instanceof McpAsyncServerExchange; } Try / catch
if (!canInvokeAsync(exchange)) {
throw new IllegalStateException("Callback requires McpAsyncServerExchange, got: " + exchange);
}
return callback.call(exchange, request); Prevention
- Always construct an async exchange from the session before dispatching
- Never pass null exchange into annotated method callbacks
- In tests, build a real McpAsyncServerExchange rather than a mock wrapper
When it happens
Trigger: Calling the async resource method callback with a null exchange, or an exchange that is neither McpAsyncServerExchange nor convertible (e.g., only a McpSyncServerExchange or unrelated object) when the method signature requires McpAsyncServerExchange.
Common situations: Manually invoking the callback from tests or custom transport glue with the wrong exchange object; a custom server integration that forgets to build an async exchange before dispatch.
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: ${exchangeType} for Async m
- Async prompt method must not declare parameter of type: ${pa
- Unsupported exchange type: ${exchangeType} for method: ${met
- Method must return either ReadResourceResult, List<ResourceC
- Unsupported Sync exchange type: ${exchangeType} for Sync met
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/aa530329859646bf.
Report an issue: GitHub.