{"record":{"id":"c3ead62d5122024b","repo":"Hmbown/CodeWhale","slug":"mcp-session-expired-error","errorCode":null,"errorMessage":"MCP session expired: {error}","messagePattern":"MCP session expired: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":2454,"sourceCode":"                    };\n                    return Err(err).with_context(|| {\n                        format!(\n                            \"Invalid MCP JSON-RPC message from server '{}': {}\",\n                            self.name, preview\n                        )\n                    });\n                }\n            };\n\n            // Check if this is a response with the expected id. We emit\n            // string IDs because some MCP gateways reject numeric JSON-RPC\n            // IDs, but accept numeric echoes for compatibility with older\n            // servers and tests.\n            if response_id_matches(value.get(\"id\"), &expected_id) {\n                if let Some(error) = value.get(\"error\")\n                    && is_mcp_stale_session_body(&error.to_string())\n                {\n                    anyhow::bail!(\"MCP session expired: {error}\");\n                }\n                return Ok(value);\n            }\n            // Skip notifications (no id) and responses with different ids\n        }\n    }\n\n    /// Gracefully close the connection\n    #[allow(dead_code)] // Public API for MCP consumers\n    pub fn close(&mut self) {\n        self.cancel_token.cancel();\n        self.state = ConnectionState::Disconnected;\n    }\n\n    fn catalog_authorized(&self) -> bool {\n        self.config\n            .reviewed_plugin\n            .as_ref()","sourceCodeStart":2436,"sourceCodeEnd":2472,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp.rs#L2436-L2472","documentation":"The MCP server replied with a JSON-RPC error object whose body matches the stale-session detector (`is_mcp_stale_session_body`) — typically an HTTP-streamable server whose session id expired server-side. The client fails the pending request with this error so higher layers can re-handshake and retry with a fresh session.","triggerScenarios":"A request over an HTTP/SSE MCP transport where the response `error` text matches stale-session signatures (e.g. 'Session not found', expired session id) while the response id matches the expected id.","commonSituations":"Server restarted or load-balancer routed to a different instance, session TTL elapsed during a long idle period, proxy dropped session affinity.","solutions":["Reconnect/re-handshake the MCP connection to obtain a fresh session id, then retry the request.","Reduce idle time between requests or configure server keepalives.","Check server session TTL settings and raise them if operations are long-lived.","If behind a proxy/load balancer, enable sticky sessions for the MCP endpoint."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = op(&mut conn).await {\n    if e.to_string().starts_with(\"MCP session expired\") {\n        conn = McpConnection::connect(name, cfg).await?; // fresh session\n        return op(&mut conn).await;\n    }\n    return Err(e);\n}","preventionTips":["Keep session affinity when routing through load balancers/proxies.","Avoid long idle gaps on HTTP-streamable MCP transports or send periodic keepalives.","Match client reconnect-on-stale-session handling to the server's session TTL."],"tags":["mcp","session","http","retry"],"backgroundTag":"jwt-token-expired","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}