{"record":{"id":"39a4ad7f74cfeb70","repo":"Hmbown/CodeWhale","slug":"failed-to-call-mcp-method-connection-is","errorCode":null,"errorMessage":"Failed to call MCP method '{}': connection '{}' is not ready","messagePattern":"Failed to call MCP method '(.+?)': connection '(.+?)' is not ready","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":2066,"sourceCode":"            \"prompts/get\",\n            serde_json::json!({\n                \"name\": prompt_name,\n                \"arguments\": arguments\n            }),\n            timeout_secs,\n        )\n        .await\n    }\n\n    /// Generic method to call an MCP method\n    async fn call_method(\n        &mut self,\n        method: &str,\n        params: serde_json::Value,\n        timeout_secs: u64,\n    ) -> Result<serde_json::Value> {\n        if self.state != ConnectionState::Ready {\n            anyhow::bail!(\n                \"Failed to call MCP method '{}': connection '{}' is not ready\",\n                method,\n                self.name\n            );\n        }\n        if let Some(source) = self.config.reviewed_plugin.as_ref() {\n            source.validate_before_use(&self.name, method)?;\n        }\n\n        let call_id = self.next_id();\n        if let Err(error) = self\n            .send(serde_json::json!({\n                \"jsonrpc\": \"2.0\",\n                \"id\": &call_id,\n                \"method\": method,\n                \"params\": params\n            }))\n            .await","sourceCodeStart":2048,"sourceCodeEnd":2084,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/mcp.rs#L2048-L2084","documentation":"call_method is the shared path behind call_tool / read_resource / get_prompt and refuses to send when the connection state is anything but Ready (crates/tui/src/mcp.rs:2065-2071). State is Connecting until initialize and discover_all finish inside connect_with_policy, and any send/recv error flips it to Disconnected via finish_guarded_error (or explicit shutdown), after which every further call fails fast with this message instead of hanging.","triggerScenarios":"Invoking tools during the connect window; reusing a connection after a prior call errored (state already Disconnected, see mcp.rs:2173-2264); calling after shutdown_all.","commonSituations":"Embedding code missing an is_ready() gate; retry loops hammering a dead connection instead of reconnecting; startup races where calls are dispatched before connect returns.","solutions":["Gate calls on the public accessors: conn.is_ready() or conn.state() == ConnectionState::Ready (mcp.rs:2149/2160).","On Disconnected, drop the connection and reconnect via connect_with_policy rather than retrying calls on it.","For one-shot flows, construct the connection and let connect_with_policy return (it only returns Ready connections) before issuing calls."],"exampleFix":"// before: may hit \"connection is not ready\" after an earlier error\nlet resp = conn.call_tool(\"search\", args, 30).await?;\n// after: check readiness and reconnect when the connection dropped\nif !conn.is_ready() {\n    conn = McpConnection::connect_with_policy(name, cfg, &timeouts, policy).await?;\n}\nlet resp = conn.call_tool(\"search\", args, 30).await?;","handlingStrategy":"type-guard","validationCode":"if !conn.is_ready() {\n    // Connecting: wait for connect_with_policy to return Ready.\n    // Disconnected: rebuild the connection instead of calling.\n}","typeGuard":"use crate::mcp::ConnectionState;\n\nfn connection_ready(conn: &McpConnection) -> bool {\n    matches!(conn.state(), ConnectionState::Ready)\n}","tryCatchPattern":"match conn.call_tool(tool, args, timeout).await {\n    Ok(value) => value,\n    Err(err) if err.to_string().contains(\"is not ready\") => {\n        // State moved to Connecting/Disconnected: reconnect, do not re-call blindly.\n        *conn = McpConnection::connect_with_policy(name, cfg, &timeouts, policy).await?;\n        conn.call_tool(tool, args, timeout).await?\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Gate every call_tool/read_resource/get_prompt on is_ready().","Treat Disconnected as terminal for that handle: reconnect, never re-call.","Let connect_with_policy fully return before dispatching calls at startup."],"tags":["mcp","lifecycle","state-machine"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}