spring-projects/spring-ai · error · IllegalArgumentException
Unsupported exchange type: ${exchangeType} for method: ${met
Error message
Unsupported exchange type: ${exchangeType} for method: ${method} in ${declaringClass} What it means
The fall-through throw in AsyncStatelessMcpResourceMethodCallback.assignExchangeType: the exchange argument is neither a McpTransportContext, McpSyncServerExchange, nor McpAsyncServerExchange (it may also be null), so the stateless callback cannot bind it to the method's McpTransportContext parameter.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/AsyncStatelessMcpResourceMethodCallback.java:91
@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 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 context The transport context, 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 result
* @throws McpError if there is an error invoking the resource method
* @throws IllegalArgumentException if the request is null or if URI variable
* extraction fails
*/
@OverrideView on GitHub (pinned to 98a7beda4f)
Solutions
- Pass a valid McpTransportContext when calling the callback (e.g., McpTransportContext.empty() in tests).
- Fix the dispatcher so it always derives the transport context from the incoming HTTP request.
- Handle null exchanges explicitly before invoking the annotated method.
Example fix
// before callback.call(null, request); // after callback.call(McpTransportContext.empty(), request);
Defensive patterns
Strategy: type-guard
Validate before calling
if (exchange == null) throw new IllegalArgumentException("McpTransportContext is required by stateless resource callbacks"); Type guard
static boolean hasTransportContext(Object ex) { return ex instanceof McpTransportContext; } Try / catch
if (!(exchange instanceof McpTransportContext ctx)) {
exchange = McpTransportContext.empty(); // or fail fast
}
return callback.call(ctx, request); Prevention
- Always build a McpTransportContext from the HTTP request before dispatch
- Use McpTransportContext.empty() in unit tests
- Never pass arbitrary objects or null as the exchange argument
When it happens
Trigger: Manually invoking the stateless resource callback with a null exchange or an unrelated object type; custom transport code that builds its own exchange object.
Common situations: Unit tests calling the callback directly without constructing a McpTransportContext; custom HTTP layer passing a request object instead of a transport context.
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 Sync exchange type: ${exchangeType} for Sync met
- Stateless Streamable-Http prompt method must not declare par
- Method must return either GetPromptResult, List<PromptMessag
- Unsupported Async exchange type: ${exchangeType} for Async m
- Unsupported exchange type: ${exchangeType} for Async method:
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/4fdcde2ebd4a6a90.
Report an issue: GitHub.