{"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":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":2254,"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":2236,"sourceCodeEnd":2272,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp.rs#L2236-L2272","documentation":"Requests can only be sent to an MCP connection whose state is ConnectionState::Ready (initialization handshake completed). Calling a method on a connection still connecting, failed, or already torn down bails with this error naming the method and connection. It is a state-machine guard, not a network failure.","triggerScenarios":"Calling any MCP method (tools/call, tools/list, ping, etc.) before initialization completes, after a connection error transitioned state away from Ready, or after the transport was shut down/cancelled.","commonSituations":"A race where a tool invocation fires while the server is still starting (slow npx/npm cold start); the server crashed earlier and the stale connection object is reused; shutdown/cancel racing a pending call.","solutions":["Ensure the MCP server finished initializing before issuing calls (await the ready signal)","Reconnect/restart the MCP server if its state left Ready","Check the server's stderr/logs for a crash that moved the connection out of Ready","Increase startup timeout or reduce server cold-start time (e.g. pre-install npx packages)"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// only issue calls after the ready signal\nconnection.ready().await?; // awaits ConnectionState::Ready\nlet value = connection.call(\"tools/call\", params, timeout).await?;","typeGuard":"fn is_ready(c: &McpConnection) -> bool { c.state() == ConnectionState::Ready }","tryCatchPattern":"match conn.call(method, params, timeout).await {\n    Err(e) if e.to_string().contains(\"is not ready\") => {\n        conn.reconnect().await?; // then retry once\n        conn.call(method, params, timeout).await\n    }\n    other => other,\n}","preventionTips":["Always await readiness before first call","Watch server logs for crashes that flip state","Handle slow cold starts (npx downloads) with generous init timeouts"],"tags":["mcp","connection-state","race-condition"],"backgroundTag":"invalid-state-transition","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"}