{"record":{"id":"fee4d4b8b1f7c5aa","repo":"spring-projects/spring-ai","slug":"unsupported-exchange-type-exchangetype-for-met-fee4d4","errorCode":null,"errorMessage":"Unsupported exchange type: ${exchangeType} for method: ${method} in ${declaringClass}","messagePattern":"Unsupported exchange type: (.+?) for method: (.+?) in (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/SyncStatelessMcpPromptMethodCallback.java","lineNumber":81,"sourceCode":"\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\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 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 GetPromptResult apply(McpTransportContext context, GetPromptRequest request) {\n\t\tif (request == null) {","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/SyncStatelessMcpPromptMethodCallback.java#L63-L99","documentation":"The sync stateless prompt callback was handed an exchange object that is neither McpSyncServerExchange, McpAsyncServerExchange, nor a recognized transport context, or the exchange is null in a context where a type is required. assignExchangeType exhausts its instanceof chain and throws IllegalArgumentException naming the actual class (or \"null\").","triggerScenarios":"Calling assignExchangeType/apply with an exchange of an unknown class or null in stateless mode where the code path still expects a recognized exchange wrapper; e.g. a custom transport passing a raw context object.","commonSituations":"Custom transport implementations feeding unexpected exchange types into annotation callbacks; upgrading the MCP SDK and passing a new exchange type the annotation layer doesn't recognize; null exchange with a method that requires context extraction.","solutions":["Ensure the caller passes McpSyncServerExchange, McpAsyncServerExchange, or McpTransportContext as expected by the sync stateless callback.","Check that the server type (sync vs async) matches the callback type being invoked.","If exchange may be null, verify your transport setup for the stateless Streamable-Http mode; the request must carry a recognized exchange/context."],"exampleFix":"// before\ncallback.assignExchangeType(someCustomExchange);\n\n// after\ncallback.assignExchangeType(new McpSyncServerExchange(transportSession));","handlingStrategy":"type-guard","validationCode":"if (exchange == null && callbackRequiresExchange) {\n    throw new IllegalArgumentException(\"Exchange must not be null for stateless prompt callback\");\n}","typeGuard":"static boolean isRecognizedExchange(Object exchange) {\n    return exchange instanceof McpSyncServerExchange\n        || exchange instanceof McpAsyncServerExchange\n        || exchange instanceof McpTransportContext;\n}","tryCatchPattern":"try {\n    GetPromptResult result = callback.apply(context, request);\n} catch (IllegalArgumentException e) {\n    log.error(\"Unsupported exchange type for stateless prompt callback: {}\", e.getMessage());\n}","preventionTips":["Only pass McpSyncServerExchange, McpAsyncServerExchange, or McpTransportContext into annotation callbacks.","Custom transports should build framework-recognized exchange/context objects.","Check SDK version compatibility after upgrades to ensure exchange types still match."],"tags":["mcp","java","exchange-type","unsupported-type"],"backgroundTag":"unsupported-operation","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}