{"record":{"id":"d8b2d3c9b31a88b4","repo":"Hmbown/CodeWhale","slug":"failed-to-store-mcp-connection-for-server-name","errorCode":null,"errorMessage":"Failed to store MCP connection for {server_name}","messagePattern":"Failed to store MCP connection for (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":3229,"sourceCode":"            Err(error) => {\n                self.note_connect_failure(server_name, &error);\n                if let Some(previous) = previous_connection {\n                    tracing::debug!(\n                        target: \"mcp\",\n                        server = %server_name,\n                        \"reconnect failed; restored the previous MCP connection and its last-good catalog\"\n                    );\n                    self.connections.insert(server_name.to_string(), previous);\n                }\n                return Err(error);\n            }\n        };\n        connection.catalog_generation = self.catalog_generation.load(Ordering::SeqCst);\n\n        self.store_ready_connection(server_name.to_string(), connection)?;\n        self.connections\n            .get_mut(server_name)\n            .ok_or_else(|| anyhow::anyhow!(\"Failed to store MCP connection for {server_name}\"))\n    }\n\n    /// Retry exactly one server against the configuration already owned by\n    /// this pool.\n    ///\n    /// Unlike normal lazy tool dispatch, an explicit row retry must not notice\n    /// a concurrent config mtime and invalidate healthy siblings. Config edits\n    /// remain owned by the explicit reload path; this operation only replaces\n    /// the named transport.\n    pub async fn retry_connection(&mut self, server_name: &str) -> Result<&mut McpConnection> {\n        self.require_server(server_name)?;\n        // A person asked for this one by name. Clear the cooldown so the\n        // attempt happens now and, if it fails again, the ladder restarts\n        // from the short end rather than from wherever it had climbed to.\n        // Explicit intent restarts supervision: the cooldown, the dead mark,\n        // and any park all clear, so the supervisor resumes watching whatever\n        // this retry stores — or stays quiet while the server is connectionless.\n        self.connect_backoff.remove(server_name);","sourceCodeStart":3211,"sourceCodeEnd":3247,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp.rs#L3211-L3247","documentation":"store_ready_connection() succeeded in inserting the newly connected McpConnection, but the immediately following self.connections.get_mut(server_name) — used to return a mutable handle — found nothing. This is an internal race/invariant break: the just-stored connection was removed between the two steps.","triggerScenarios":"Concurrent code removing or taking entries from pool.connections (teardown, reconnect of another path, dynamic server removal) in the window between store_ready_connection and get_mut.","commonSituations":"Pool shutdown racing a lazy connect; two tasks reconnecting the same server simultaneously; dynamic-server eviction concurrent with first use.","solutions":["Retry the call; the pool will reconnect on demand.","Serialize reconnect/store operations for a given server under one lock.","Return the connection directly from store_ready_connection instead of re-fetching from the map.","If it reproduces without concurrency, file a bug with logs."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match pool.call_tool(server, tool, input) {\n    Err(e) if e.to_string().contains(\"Failed to store MCP connection\") => {\n        // race with pool mutation: retry; the pool will reconnect\n        pool.call_tool(server, tool, input).await?\n    }\n    other => other?,\n}","preventionTips":["Avoid teardown/reconnection of the pool while calls are in flight.","Hold a single lock across store and lookup for a given server.","Return the stored handle from the store function rather than re-fetching.","Retry once on this error; it is a transient race, not a config problem."],"tags":["mcp","race-condition","invariant"],"backgroundTag":"internal-invariant-violation","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"}