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 arguments

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Invoke the resource through the stateless Streamable-Http server path so a McpTransportContext is supplied.
  2. Detach the provider from the session-based sync server registration.
  3. 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

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


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/a835256a083fe79e. Report an issue: GitHub.