{"record":{"id":"42200714feabd31f","repo":"apache/shenyu","slug":"no-serverwebexchange-found-for-session-sessionid-it-should","errorCode":null,"errorMessage":"No ServerWebExchange found for session '${sessionId}'. It should have been stored by handleMessageEndpoint before the tool was invoked.","messagePattern":"No ServerWebExchange found for session '(.+?)'\\. It should have been stored by handleMessageEndpoint before the tool was invoked\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java","lineNumber":813,"sourceCode":"        return exception instanceof IllegalStateException\n                && StringUtils.hasText(exception.getMessage())\n                && exception.getMessage().startsWith(SDK_COMPATIBILITY_ERROR_PREFIX);\n    }\n\n    /**\n     * Gets the origin ServerWebExchange for the given session ID.\n     *\n     * @param sessionId the session ID\n     * @return the origin ServerWebExchange\n     * @throws IllegalStateException if exchange cannot be retrieved\n     */\n    private ServerWebExchange getOriginExchange(final String sessionId) {\n        final ServerWebExchange exchange = ShenyuMcpExchangeHolder.get(sessionId);\n        if (Objects.nonNull(exchange)) {\n            LOG.debug(\"Found existing exchange for session: {}\", sessionId);\n            return exchange;\n        }\n        throw new IllegalStateException(\"No ServerWebExchange found for session '\" + sessionId\n                + \"'. It should have been stored by handleMessageEndpoint before the tool was invoked.\");\n    }\n}\n","sourceCodeStart":795,"sourceCodeEnd":817,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java#L795-L817","documentation":"Thrown by ShenyuToolCallback.getOriginExchange when ShenyuMcpExchangeHolder.get(sessionId) returns null. The MCP HTTP message endpoint (handleMessageEndpoint) must store the originating gateway ServerWebExchange in ShenyuMcpExchangeHolder before a tool is invoked; without it the tool cannot access the original request context (headers, params) to proxy the backend call. This means the session-to-exchange mapping is missing from the holder.","triggerScenarios":"ShenyuMcpExchangeHolder.get(sessionId) returns null for the session id extracted from the MCP exchange — the exchange was never stored, was removed on session close, or a different session id is being looked up.","commonSituations":"Calling tools outside the normal HTTP message flow (unit/integration tests, manual invocations); holder cleanup (session expiry/restart) removing the entry before the tool callback runs; load-balanced deployments where the MCP message landed on a different instance than the tool execution; custom routing bypassing handleMessageEndpoint.","solutions":["Ensure requests flow through handleMessageEndpoint so the ServerWebExchange is registered in ShenyuMcpExchangeHolder before the tool executes","Verify the sessionId used is the one obtained via McpSessionHelper.getSessionId from the current McpSyncServerExchange, not a stale/foreign id","Check ShenyuMcpExchangeHolder cleanup/expiry logic and gateway restarts — single-instance sticky routing is required since the holder is in-memory","Call ShenyuMcpExchangeHolder.get(sessionId) yourself before invoking the tool and fail fast with a clear client error"],"exampleFix":"// before\ncallback.call(request);\n// after: guard before invocation\nif (ShenyuMcpExchangeHolder.get(sessionId) == null) {\n    throw new McpError(\"no exchange for session \" + sessionId);\n}\ncallback.call(request);","handlingStrategy":"validation","validationCode":"ServerWebExchange origin = ShenyuMcpExchangeHolder.get(sessionId);\nif (origin == null) {\n    throw new McpError(\"no origin exchange registered for session \" + sessionId);\n}","typeGuard":"boolean exchangeStored(String sessionId) {\n    return sessionId != null && ShenyuMcpExchangeHolder.get(sessionId) != null;\n}","tryCatchPattern":"try {\n    ServerWebExchange ex = ShenyuMcpExchangeHolder.get(sessionId);\n    // use ex\n} catch (IllegalStateException e) {\n    if (e.getMessage().startsWith(\"No ServerWebExchange found\")) {\n        LOG.warn(\"Exchange missing for session {}; client must reconnect\", sessionId);\n    } else throw e;\n}","preventionTips":["Always route tool executions through handleMessageEndpoint","Use sticky/single-instance routing since ShenyuMcpExchangeHolder is in-memory","Remove stale sessions promptly so lookups fail early, not mid-tool-call"],"tags":["mcp","session","state","http"],"backgroundTag":"entity-not-found","analyzedSha":"567142e07261b3e615ae8850b30f4421f455cc5d","analyzedAt":"2026-09-12T10:08:21.293Z","contentChangedAt":"2026-09-12T10:08:21.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}