Hmbown/CodeWhale · error · anyhow::Error

Failed to call MCP method '{}': connection '{}' is not ready

Error message

Failed to call MCP method '{}': connection '{}' is not ready

What it means

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.

Source

Thrown at crates/tui/src/mcp.rs:2066

            "prompts/get",
            serde_json::json!({
                "name": prompt_name,
                "arguments": arguments
            }),
            timeout_secs,
        )
        .await
    }

    /// Generic method to call an MCP method
    async fn call_method(
        &mut self,
        method: &str,
        params: serde_json::Value,
        timeout_secs: u64,
    ) -> Result<serde_json::Value> {
        if self.state != ConnectionState::Ready {
            anyhow::bail!(
                "Failed to call MCP method '{}': connection '{}' is not ready",
                method,
                self.name
            );
        }
        if let Some(source) = self.config.reviewed_plugin.as_ref() {
            source.validate_before_use(&self.name, method)?;
        }

        let call_id = self.next_id();
        if let Err(error) = self
            .send(serde_json::json!({
                "jsonrpc": "2.0",
                "id": &call_id,
                "method": method,
                "params": params
            }))
            .await

View on GitHub (pinned to 8880682c63)

Solutions

  1. Gate calls on the public accessors: conn.is_ready() or conn.state() == ConnectionState::Ready (mcp.rs:2149/2160).
  2. On Disconnected, drop the connection and reconnect via connect_with_policy rather than retrying calls on it.
  3. For one-shot flows, construct the connection and let connect_with_policy return (it only returns Ready connections) before issuing calls.

Example fix

// before: may hit "connection is not ready" after an earlier error
let resp = conn.call_tool("search", args, 30).await?;
// after: check readiness and reconnect when the connection dropped
if !conn.is_ready() {
    conn = McpConnection::connect_with_policy(name, cfg, &timeouts, policy).await?;
}
let resp = conn.call_tool("search", args, 30).await?;
Defensive patterns

Strategy: type-guard

Validate before calling

if !conn.is_ready() {
    // Connecting: wait for connect_with_policy to return Ready.
    // Disconnected: rebuild the connection instead of calling.
}

Type guard

use crate::mcp::ConnectionState;

fn connection_ready(conn: &McpConnection) -> bool {
    matches!(conn.state(), ConnectionState::Ready)
}

Try / catch

match conn.call_tool(tool, args, timeout).await {
    Ok(value) => value,
    Err(err) if err.to_string().contains("is not ready") => {
        // State moved to Connecting/Disconnected: reconnect, do not re-call blindly.
        *conn = McpConnection::connect_with_policy(name, cfg, &timeouts, policy).await?;
        conn.call_tool(tool, args, timeout).await?
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/39a4ad7f74cfeb70. Report an issue: GitHub.