{"record":{"id":"c895ef78a7465f0e","repo":"Hmbown/CodeWhale","slug":"mcp-session-preflight-cancelled-after-plugin-autho","errorCode":null,"errorMessage":"MCP session preflight cancelled after plugin authority changed","messagePattern":"MCP session preflight cancelled after plugin authority changed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/tui/src/mcp/http.rs","lineNumber":191,"sourceCode":"    ///   line and move on — the `initialize` POST will proceed without a\n    ///   session ID.\n    /// * If the server opens an SSE stream in response (the GET from old\n    ///   SSE transport), we read only the headers, then discard the body\n    ///   so the SSE stream is torn down. The actual SSE path uses a\n    ///   dedicated `SseTransport` and is triggered by the incompatible-\n    ///   status fallback in [`HttpTransport::send`].\n    pub(super) async fn try_establish_session(&mut self) -> Result<()> {\n        let cancel = self.cancel_token.clone();\n        let transport = match &mut self.mode {\n            HttpTransportMode::Streamable(t) => t,\n            // Already on SSE — session is implicit via the long-lived GET.\n            HttpTransportMode::Sse(_) => return Ok(()),\n        };\n\n        let headers = tokio::select! {\n            biased;\n            _ = cancel.cancelled() => {\n                anyhow::bail!(\"MCP session preflight cancelled after plugin authority changed\")\n            }\n            headers = transport.auth.resolved_headers() => headers?,\n        };\n        let request = apply_safe_custom_headers(\n            with_default_mcp_http_headers(transport.client.get(&transport.url), false),\n            &headers,\n        );\n        let response = tokio::select! {\n            biased;\n            _ = cancel.cancelled() => {\n                anyhow::bail!(\"MCP session preflight cancelled after plugin authority changed\")\n            }\n            response = tokio::time::timeout(Duration::from_secs(5), request.send()) => {\n                response\n                    .map_err(|_| anyhow::anyhow!(\"GET timeout\"))?\n                    .map_err(|e| anyhow::anyhow!(\"GET error: {e}\"))?\n            }\n        };","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp/http.rs#L173-L209","documentation":"try_establish_session races every await against a cancellation token fired when the connection's plugin authority changes (the connection is being recycled). This first bail fires when cancellation wins while resolving auth headers: the streamable session preflight is abandoned on purpose because the connection being built is already obsolete.","triggerScenarios":"A plugin re-review or re-stage changes its authority while a streamable HTTP connection to it is establishing; drop_connection runs concurrently with the preflight's header resolution.","commonSituations":"Editing or reviewing a plugin while a session to it is connecting; rapid reconnect churn during config reload.","solutions":["Retry the operation against a fresh connection - the old one is intentionally dead","Avoid re-reviewing plugins while calls to them are in flight","If constant, look for code that keeps churning plugin authority or dropping connections in a loop"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match transport.try_establish_session().await {\n    Err(e) if e.to_string().contains(\"preflight cancelled after plugin authority changed\") => {\n        // obtain a fresh connection (the old one was recycled) and retry once\n    }\n    other => other,\n}","preventionTips":["Do not re-review or re-stage plugins while connections to them are being established","Drop cached connections immediately after authority changes so preflights stop on stale transports","Treat this as a transient race: a single retry on a fresh connection should succeed"],"tags":["mcp","concurrency","cancellation","plugins"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}