spring-projects/spring-ai · error · java.lang.IllegalArgumentException

Async prompt method must not declare parameter of type: {par

Error message

Async prompt method must not declare parameter of type: {paramType.getName()}. Use McpAsyncServerExchange instead. Method: {this.method.getName()} in {this.method.getDeclaringClass().getName()}

What it means

AsyncMcpPromptMethodCallback.validateParamType rejects McpSyncServerExchange parameters in methods bound to an async (reactive) prompt callback. A synchronous exchange cannot be provided to an async handler, so registration fails immediately with this IllegalArgumentException, directing you to use McpAsyncServerExchange instead.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AsyncMcpPromptMethodCallback.java:55

 *
 * This class provides a way to convert methods annotated with {@link McpPrompt} into
 * callback functions that can be used to handle prompt requests asynchronously. It
 * supports various method signatures and return types.
 *
 * @author Christian Tzolov
 */
public final class AsyncMcpPromptMethodCallback extends AbstractMcpPromptMethodCallback
		implements BiFunction<McpAsyncServerExchange, GetPromptRequest, Mono<GetPromptResult>> {

	private AsyncMcpPromptMethodCallback(Builder builder) {
		super(builder.method, builder.bean, builder.prompt);
	}

	@Override
	protected void validateParamType(Class<?> paramType) {

		if (McpSyncServerExchange.class.isAssignableFrom(paramType)) {
			throw new IllegalArgumentException("Async prompt method must not declare parameter of type: "
					+ paramType.getName() + ". Use McpAsyncServerExchange 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 Async exchange type: "
						+ syncServerExchange.getClass().getName() + " for Async method: " + method.getName() + " in "
						+ method.getDeclaringClass().getName());

			}

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Replace McpSyncServerExchange with McpAsyncServerExchange in the async method signature.
  2. If the method truly needs synchronous semantics, register it with the sync callback/server variant (McpSyncServer) instead.
  3. Use McpTransportContext if you only need request metadata/transport context, not the full exchange.

Example fix

// before
@McpPrompt(name = "explain")
public Mono<String> explain(McpSyncServerExchange exchange, @McpArg String topic) { ... }
// after
@McpPrompt(name = "explain")
public Mono<String> explain(McpAsyncServerExchange exchange, @McpArg String topic) { ... }
Defensive patterns

Strategy: validation

Validate before calling

for (Parameter p : method.getParameters()) {
    if (isAsyncHandler && McpSyncServerExchange.class.isAssignableFrom(p.getType())) {
        throw new IllegalStateException("Async method uses McpSyncServerExchange: " + method);
    }
}

Prevention

When it happens

Trigger: Registering an @McpPrompt method on an async server (or processed by the async callback) with signature `myPrompt(McpSyncServerExchange exchange, ...)`. validateParameters calls validateParamType for every parameter and throws on the sync exchange type.

Common situations: Copying a sync handler to an async server without changing the exchange type; migrating a server from sync to async transport; shared utility prompt classes used by both sync and async servers.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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