Hmbown/CodeWhale · error · anyhow::Error

MCP connection disappeared for {server_name}

Error message

MCP connection disappeared for {server_name}

What it means

In get_or_connect, the code first checks connections.get(server_name) and reads is_ready(); if ready it immediately calls connections.get_mut(server_name). Because &mut self borrows the map exclusively between the two lookups and no code runs in between that could remove the entry, a None here would require the map to lose an entry with no mutation — i.e. a broken internal invariant (concurrent aliasing or memory corruption), not a user condition.

Source

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

                    .and_then(|config| config.reviewed_plugin.clone())
            });
        if let Some(source) = plugin_source
            && let Err(error) = source.validate_before_use(server_name, "use")
        {
            self.drop_connection(server_name, "plugin authority revoked or changed");
            return Err(error);
        }

        let is_ready = self
            .connections
            .get(server_name)
            .map(|conn| conn.is_ready())
            .unwrap_or(false);
        if is_ready {
            return self
                .connections
                .get_mut(server_name)
                .ok_or_else(|| anyhow::anyhow!("MCP connection disappeared for {server_name}"));
        }

        self.drop_connection(server_name, "reconnect");

        // Check static config first, then dynamic servers
        let server_config = self
            .config
            .servers
            .get(server_name)
            .cloned()
            .or_else(|| self.dynamic_servers.read().get(server_name).cloned())
            .ok_or_else(|| anyhow::anyhow!("Failed to find MCP server: {server_name}"))?;

        if !server_config.is_enabled() {
            anyhow::bail!("Failed to connect MCP server '{server_name}': server is disabled");
        }

        let mut connection = McpConnection::connect_with_policy(

View on GitHub (pinned to 8880682c63)

Solutions

  1. Report it as an internal bug against the mcp module with the reproduction steps; do not try to fix it via config.
  2. If hacking on the pool: audit for any await, lock, or removal inserted between the is_ready check and get_mut in get_or_connect.
  3. Retry via get_or_connect once — if it persists, the connections map is genuinely inconsistent and the process state is suspect.
Defensive patterns

Strategy: validation

Try / catch

// Rust: invariant breach — report, attempt one clean retry
match pool.get_or_connect(name).await {
    Err(e) if e.to_string().contains("connection disappeared") => {
        tracing::error!("internal invariant broken in McpPool: {e:#}");
        pool.get_or_connect(name).await // one retry on a possibly-consistent map
    }
    o => o,
}

Prevention

When it happens

Trigger: Effectively unreachable in correct code: requires the HashMap to yield Some on get and None on get_mut for the same key with no intervening mutation, which cannot happen under the exclusive borrow.

Common situations: Not hit in practice; would only appear if a future refactor moved an await or a removal between the readiness check and the get_mut, or if unsafe aliasing of the pool existed. Seeing it means an internal regression, not a configuration problem.

Related errors


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