apache/shenyu · error · IllegalStateException
Session ID is empty – it should have been set earlier by…
Error message
Session ID is empty – it should have been set earlier by handleMessageEndpoint
What it means
Thrown by ShenyuToolCallback.extractSessionId when McpSessionHelper.getSessionId() returns a null/blank session ID. The MCP tool invocation pipeline requires the MCP session id (established when the HTTP endpoint handleMessageEndpoint handled the client's request) to correlate the tool call back to the originating gateway ServerWebExchange; a blank id means the internal session state was not propagated. This is an internal invariant violation rather than a caller-input problem.
Solutions
- Ensure the tool is invoked only through the MCP message endpoint (handleMessageEndpoint) after a proper initialize handshake so the session id is set
- Verify the MCP SDK (tested: 0.17.0) matches the version ShenYu was built against — a different version can produce exchanges with null session state
- Log the McpSyncServerExchange state (via McpSessionHelper.getSession) to confirm the session object and its id before the call
- Check ShenyuMcpExchangeHolder/handleMessageEndpoint wiring is intact and no custom filter stripped the session attribute
Example fix
// before: invoking tool directly without session
new ShenyuToolCallback(...).call(args);
// after: route the call through the MCP message endpoint with an initialized session
mcpClient.initialize(...); mcpClient.callTool("myTool", args); Defensive patterns
Strategy: validation
Validate before calling
String sessionId = McpSessionHelper.getSessionId(mcpExchange);
if (sessionId == null || sessionId.isBlank()) {
throw new McpError("MCP session not initialized; invoke tools via handleMessageEndpoint");
} Type guard
boolean hasSession(McpSyncServerExchange ex) {
try { return McpSessionHelper.getSession(ex) != null && StringUtils.hasText(McpSessionHelper.getSession(ex).getId()); }
catch (RuntimeException e) { return false; }
} Try / catch
try {
callback.call(args, toolContext);
} catch (IllegalStateException e) {
if (e.getMessage().contains("Session ID is empty")) {
LOG.error("MCP session state missing; re-handshake required", e);
// return JSON-RPC error to client
} else throw e;
} Prevention
- Only invoke tools through the MCP message endpoint after a successful initialize handshake
- Keep the MCP SDK at the tested 0.17.0 version
- Add a startup health check asserting McpSessionHelper.isReflectionAvailable()
When it happens
Trigger: McpSessionHelper.getSessionId(mcpExchange) returns null or empty string during a tool callback execution — i.e. the McpSyncServerExchange exists but its underlying McpServerSession has no id, or the session was not established by handleMessageEndpoint before the tool was invoked.
Common situations: Invoking a tool outside a normal MCP HTTP message flow (e.g. programmatic/direct tool invocation in tests); a partially-initialized McpSyncServerExchange built without a session; a gateway restart or session teardown between handleMessageEndpoint and the tool call.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Failed to retrieve MCP sync server exchange from context
- No ServerWebExchange found for session
- Session is required in McpAsyncServerExchange
- Import mcp server config failed:
- OpenAPI document is missing the top-level 'servers' field…
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/e7f68549d5e0e7a3.
Report an issue: GitHub.
Appendix: source
Thrown at shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java:779
return exchange;
}
/**
* Extracts the session ID from the MCP sync server exchange.
*
* @param mcpExchange the MCP sync server exchange
* @return the session ID
* @throws IllegalStateException if the session ID is blank or an SDK compatibility issue blocks extraction
* @throws IllegalArgumentException if the exchange is missing required session state
*/
private String extractSessionId(final McpSyncServerExchange mcpExchange) {
try {
final String sessionId = McpSessionHelper.getSessionId(mcpExchange);
if (StringUtils.hasText(sessionId)) {
LOG.debug("Extracted session ID: {}", sessionId);
return sessionId;
}
throw new IllegalStateException("Session ID is empty – it should have been set earlier by handleMessageEndpoint");
} catch (RuntimeException e) {
if (!isSdkCompatibilityError(e)) {
throw e;
}
// Re-throw SDK compatibility errors with additional context.
throw new IllegalStateException(
"Failed to extract session ID from MCP exchange. "
+ "This may indicate an SDK compatibility issue. "
+ "Tested SDK version: " + McpSessionHelper.getSupportedSdkVersion() + ". "
+ "Original error: " + e.getMessage(), e);
}
}
private boolean isSdkCompatibilityError(final RuntimeException exception) {
return exception instanceof IllegalStateException
&& StringUtils.hasText(exception.getMessage())
&& exception.getMessage().startsWith(SDK_COMPATIBILITY_ERROR_PREFIX);View on GitHub (pinned to 567142e072)