spring-projects/spring-ai · error · IllegalArgumentException
Unsupported Sync exchange type: ${exchangeType} for Sync met
Error message
Unsupported Sync exchange type: ${exchangeType} for Sync method: ${method} in ${declaringClass} What it means
Thrown by AsyncStatelessMcpResourceMethodCallback.assignExchangeType when the stateless method expects a McpTransportContext but the object supplied as the exchange is a McpSyncServerExchange. A sync session exchange is incompatible with the stateless Streamable-Http path, which must operate without a server session.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/AsyncStatelessMcpResourceMethodCallback.java:81
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 extracts URI variable values from the request URI, builds the argumentsView on GitHub (pinned to 98a7beda4f)
Solutions
- Invoke the resource through the stateless Streamable-Http server path so a McpTransportContext is supplied.
- Detach the provider from the session-based sync server registration.
- If sync session semantics are truly needed, do not use the stateless callback; register with a stateful server.
Example fix
// before syncServer.dispatch(provider); // passes McpSyncServerExchange // after statelessStreamableHttpServer.dispatch(provider); // passes McpTransportContext
Defensive patterns
Strategy: try-catch
Validate before calling
if (exchange instanceof McpSyncServerExchange) {
throw new IllegalStateException("Stateless callback must not receive a sync session exchange");
} Type guard
static boolean isSync(Object ex) { return ex instanceof McpSyncServerExchange; } Try / catch
try {
return callback.call(ctx, request);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Unsupported Sync exchange type")) {
throw new IllegalStateException("Wrong dispatcher for stateless provider", e);
}
throw e;
} Prevention
- Keep stateless providers registered only on stateless Streamable-Http servers
- Do not share provider instances between session-based and stateless servers
- Verify dispatcher wiring in integration tests
When it happens
Trigger: Dispatching a resource call through the stateless Streamable-Http callback while passing a McpSyncServerExchange as the exchange argument (method signature declares McpTransportContext).
Common situations: Calling a stateless-registered provider from a session-based (sync) server; mixing spec builders so the wrong dispatcher invokes the method.
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 exchange type: ${exchangeType} for method: ${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/a835256a083fe79e.
Report an issue: GitHub.