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 result

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Pass an McpAsyncServerExchange when invoking the callback (build it from the async server session).
  2. If you only have a transport context, change the method parameter to McpTransportContext.
  3. 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

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


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