{"record":{"id":"8e768b49a5f366ee","repo":"spring-projects/spring-ai","slug":"unsupported-async-exchange-type-asyncserverexcha","errorCode":null,"errorMessage":"Unsupported Async exchange type: {asyncServerExchange.getClass().getName()} for Sync method: {method.getName()} in {method.getDeclaringClass().getName()}","messagePattern":"Unsupported Async exchange type: (.+?) for Sync method: (.+?) in (.+?)","errorType":"validation","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/SyncMcpPromptMethodCallback.java","lineNumber":72,"sourceCode":"\t\tif (McpAsyncServerExchange.class.isAssignableFrom(paramType)) {\n\t\t\tthrow new IllegalArgumentException(\"Sync prompt method must not declare parameter of type: \"\n\t\t\t\t\t+ paramType.getName() + \". Use McpSyncServerExchange instead.\" + \" Method: \" + this.method.getName()\n\t\t\t\t\t+ \" in \" + this.method.getDeclaringClass().getName());\n\t\t}\n\t}\n\n\t@Override\n\tprotected Object assignExchangeType(Class<?> paramType, Object exchange) {\n\n\t\tif (McpTransportContext.class.isAssignableFrom(paramType)) {\n\t\t\tif (exchange instanceof McpTransportContext transportContext) {\n\t\t\t\treturn transportContext;\n\t\t\t}\n\t\t\telse if (exchange instanceof McpSyncServerExchange syncServerExchange) {\n\t\t\t\treturn syncServerExchange.transportContext();\n\t\t\t}\n\t\t\telse if (exchange instanceof McpAsyncServerExchange asyncServerExchange) {\n\t\t\t\tthrow new IllegalArgumentException(\"Unsupported Async exchange type: \"\n\t\t\t\t\t\t+ asyncServerExchange.getClass().getName() + \" for Sync method: \" + method.getName() + \" in \"\n\t\t\t\t\t\t+ method.getDeclaringClass().getName());\n\t\t\t}\n\t\t}\n\t\telse if (McpSyncServerExchange.class.isAssignableFrom(paramType)) {\n\t\t\tif (exchange instanceof McpSyncServerExchange syncServerExchange) {\n\t\t\t\treturn syncServerExchange;\n\t\t\t}\n\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Unsupported exchange type: \" + (exchange != null ? exchange.getClass().getName() : \"null\")\n\t\t\t\t\t\t\t+ \" for Sync method: \" + method.getName() + \" in \" + method.getDeclaringClass().getName());\n\t\t}\n\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Unsupported exchange type: \" + (exchange != null ? exchange.getClass().getName() : \"null\")\n\t\t\t\t\t\t+ \" for method: \" + method.getName() + \" in \" + method.getDeclaringClass().getName());\n\t}","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/SyncMcpPromptMethodCallback.java#L54-L90","documentation":"When a sync prompt method declares an McpTransportContext parameter, assignExchangeType converts the incoming exchange into a context. An McpAsyncServerExchange arriving at a Sync method cannot be converted, so the callback throws IllegalArgumentException naming the unsupported async exchange type.","triggerScenarios":"A SyncMcpPromptMethodCallback-handled method with an McpTransportContext parameter is invoked while the dispatch path supplies an McpAsyncServerExchange.","commonSituations":"Registering the same annotated bean with both sync and async servers; wiring an async transport provider to a sync server.","solutions":["Register the bean with the sync MCP server so a McpSyncServerExchange is supplied","Use a separate bean/callback for the async server","Match transport provider and server types (sync provider with McpServer.sync, async with McpServer.async)"],"exampleFix":"// before\nMcpServer.async(provider).prompts(syncCallback).build();\n// after\nMcpServer.sync(provider).prompts(syncCallback).build();","handlingStrategy":"type-guard","validationCode":"if (exchange instanceof McpAsyncServerExchange) throw new IllegalStateException(\"Sync callback given async exchange\");","typeGuard":"boolean syncCompatible(Object e) { return e instanceof McpTransportContext || e instanceof McpSyncServerExchange; }","tryCatchPattern":"try { r = callback.apply(exchange, req); } catch (IllegalArgumentException e) { throw new McpError(\"Sync/async exchange mismatch\"); }","preventionTips":["Register callbacks only with servers of the matching type","Separate sync and async beans; do not reuse across servers","Integration-test each prompt through its real server transport"],"tags":["mcp","sync-async-mismatch","exchange-type","prompt-method"],"backgroundTag":"type-mismatch","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}