{"record":{"id":"e56c54fa13d7c2e4","repo":"spring-projects/spring-ai","slug":"unsupported-exchange-type-exchange-null-exc-e56c54","errorCode":null,"errorMessage":"Unsupported exchange type: {exchange != null ? exchange.getClass().getName() : \"null\"} for method: {method.getName()} in {method.getDeclaringClass().getName()}","messagePattern":"Unsupported exchange type: (.+?) for 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/AsyncStatelessMcpPromptMethodCallback.java","lineNumber":84,"sourceCode":"\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\tthrow new IllegalArgumentException(\"Unsupported Sync exchange type: \"\n\t\t\t\t\t\t+ syncServerExchange.getClass().getName() + \" for Sync method: \" + method.getName() + \" in \"\n\t\t\t\t\t\t+ method.getDeclaringClass().getName());\n\n\t\t\t}\n\t\t\telse if (exchange instanceof McpAsyncServerExchange asyncServerExchange) {\n\t\t\t\treturn asyncServerExchange.transportContext();\n\t\t\t}\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}\n\n\t/**\n\t * Apply the callback to the given context and request.\n\t * <p>\n\t * This method builds the arguments for the method call, invokes the method, and\n\t * converts the result to a GetPromptResult.\n\t * @param context The transport context, may be null if the method doesn't require it\n\t * @param request The prompt request, must not be null\n\t * @return A Mono that emits the prompt result\n\t * @throws McpError if there is an error invoking the prompt method\n\t * @throws IllegalArgumentException if the request is null\n\t */\n\t@Override\n\tpublic Mono<GetPromptResult> apply(McpTransportContext context, GetPromptRequest request) {\n\t\tif (request == null) {","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AsyncStatelessMcpPromptMethodCallback.java#L66-L102","documentation":"The fallback branch of assignExchangeType in AsyncStatelessMcpPromptMethodCallback throws when the runtime exchange object is neither McpTransportContext, McpSyncServerExchange, nor McpAsyncServerExchange (or is null). It signals the callback was invoked with an unrecognized exchange type from an unsupported transport path.","triggerScenarios":"Invoking the prompt callback with an exchange argument of an unexpected type (or null) that no branch of assignExchangeType can handle.","commonSituations":"Custom transport implementations passing a novel exchange object; misuse of the callback API by calling apply/assignExchangeType directly with the wrong argument; version mismatches where new exchange types are not yet handled.","solutions":["Pass a supported exchange type: McpTransportContext, McpSyncServerExchange, or McpAsyncServerExchange","If the exchange is null, ensure the server supplies a real exchange/transport context","Upgrade or align library versions so transport and callback layers agree on exchange types"],"exampleFix":"// before\ncallback.apply(new MyCustomExchange(), request);\n// after\ncallback.apply(mcpAsyncServerExchange, request);","handlingStrategy":"type-guard","validationCode":"if (exchange == null || !(exchange instanceof McpTransportContext || exchange instanceof McpSyncServerExchange || exchange instanceof McpAsyncServerExchange)) throw new IllegalArgumentException(\"unsupported exchange\");","typeGuard":"boolean supportedExchange(Object e) { return e instanceof McpTransportContext || e instanceof McpSyncServerExchange || e instanceof McpAsyncServerExchange; }","tryCatchPattern":"try { ctx = callback.apply(exchange, req); } catch (IllegalArgumentException e) { log.error(\"Unsupported exchange {}\", exchange == null ? \"null\" : exchange.getClass(), e); }","preventionTips":["Only invoke callbacks from the MCP framework dispatch, not directly","Pin consistent versions of mcp core and annotation modules","Wrap custom transports and map their exchange to McpTransportContext"],"tags":["mcp","exchange-type","prompt-method","internal"],"backgroundTag":"unsupported-operation","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"}