{"record":{"id":"8cd142b444096b68","repo":"Hmbown/CodeWhale","slug":"mcp-streamable-http-session-expired-retry-with-a","errorCode":null,"errorMessage":"MCP Streamable HTTP session expired; retry with a new session required ({detail})","messagePattern":"MCP Streamable HTTP session expired; retry with a new session required \\((.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/tui/src/mcp/http.rs","lineNumber":258,"sourceCode":"            HttpTransportMode::Streamable(transport) => match transport.send(msg.clone()).await {\n                Ok(()) => Ok(()),\n                Err(StreamableSendError::Incompatible(detail)) => {\n                    tracing::debug!(\n                        \"MCP Streamable HTTP unavailable; falling back to SSE endpoint discovery: {}\",\n                        detail\n                    );\n                    self.switch_to_sse_and_send(msg).await\n                }\n                Err(StreamableSendError::StaleSession(detail)) => {\n                    if let HttpTransportMode::Streamable(transport) = &mut self.mode {\n                        tracing::debug!(\n                            target: \"mcp\",\n                            error = %detail,\n                            \"MCP Streamable HTTP session expired; clearing cached session ID\"\n                        );\n                        transport.session_id = None;\n                    }\n                    Err(anyhow::anyhow!(\n                        \"MCP Streamable HTTP session expired; retry with a new session required ({detail})\"\n                    ))\n                }\n                Err(StreamableSendError::Other(err)) => Err(err),\n            },\n            HttpTransportMode::Sse(transport) => transport.send(msg).await,\n        }\n    }\n\n    async fn recv(&mut self) -> Result<Vec<u8>> {\n        match &mut self.mode {\n            HttpTransportMode::Streamable(transport) => transport.recv().await,\n            HttpTransportMode::Sse(transport) => transport.recv().await,\n        }\n    }\n\n    async fn shutdown(&mut self) {\n        if let HttpTransportMode::Sse(transport) = &mut self.mode {","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp/http.rs#L240-L276","documentation":"When a POST on a streamable HTTP transport fails because the server considers the cached Mcp-Session-Id dead, HttpTransport::send clears the cached session ID and returns this error: the caller must retry, which establishes a fresh session and new ID. The manager's call_tool already performs one such retry internally (drop connection, reconnect, resend) for stale sessions; this error surfaces when a retry path still ends in a stale session.","triggerScenarios":"A streamable HTTP server expires or forgets sessions (restart, TTL, load-balancer failover) while codewhale holds a cached session ID; the next send gets the stale-session response and the automatic retry also loses the race.","commonSituations":"Server restarts or autoscaling behind a load balancer; idle periods exceeding the server's session TTL; deployments where the session store is not shared across instances.","solutions":["Retry the tool call - a new session is established automatically on the next attempt","If it recurs often, keep the connection warm (periodic ping/list calls) or raise the server's session TTL","For load-balanced servers, enable sticky sessions or a shared session store server-side"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"let mut attempts = 0;\nloop {\n    attempts += 1;\n    match manager.call_tool(prefixed_name, args.clone()).await {\n        Ok(out) => break Ok(out),\n        Err(e) if e.to_string().contains(\"session expired; retry with a new session required\") && attempts < 3 => continue,\n        Err(e) => break Err(e),\n    }\n}","preventionTips":["Retry once or twice on this error - the cached session ID is already cleared, so the next attempt opens a fresh session","Keep streamable HTTP connections warm with periodic calls shorter than the server's session TTL","Configure sticky sessions (or a shared session store) on load-balanced MCP servers"],"tags":["mcp","http","session","retry"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}