Hmbown/CodeWhale · error · anyhow::Error

Failed to store MCP connection for {server_name}

Error message

Failed to store MCP connection for {server_name}

What it means

Immediately after connections.insert(server_name.to_string(), connection), get_or_connect calls connections.get_mut(server_name). Since insert with a freshly owned String key guarantees presence and &mut self prevents concurrent mutation, a None return contradicts the HashMap contract. Like the 'connection disappeared' branch above it is a defensive invariant check, not an expected runtime condition.

Source

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

            .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(
            server_name.to_string(),
            server_config,
            &self.config.timeouts,
            self.network_policy.as_ref(),
        )
        .await?;
        connection.catalog_generation = self.catalog_generation.load(Ordering::SeqCst);

        self.connections.insert(server_name.to_string(), connection);
        self.connections
            .get_mut(server_name)
            .ok_or_else(|| anyhow::anyhow!("Failed to store MCP connection for {server_name}"))
    }

    /// Connect to all enabled servers, returning errors for failed connections
    pub async fn connect_all(&mut self) -> Vec<(String, anyhow::Error)> {
        let mut errors = Vec::new();
        // Reload before taking the configured-name snapshot. Previously the
        // first call after adding a server captured the old names, then only
        // noticed the config change inside `get_or_connect`, delaying the new
        // server until a second turn.
        if let Err(err) = self.reload_if_config_changed().await {
            errors.push(("configuration".to_string(), err));
            return errors;
        }
        let names: Vec<String> = self
            .config
            .servers
            .keys()
            .filter(|n| self.config.servers[*n].is_enabled())

View on GitHub (pinned to 8880682c63)

Solutions

  1. Treat as an internal bug: capture logs and a reproduction and report it against the mcp module.
  2. Audit any local modifications to get_or_connect or the connections map type.
  3. Retry via get_or_connect once to see if it reproduces; persistent occurrence indicates corrupted state — restart the session.
Defensive patterns

Strategy: validation

Try / catch

// Rust: internal invariant — report and retry once
match pool.get_or_connect(name).await {
    Err(e) if e.to_string().contains("Failed to store MCP connection") => {
        tracing::error!("internal HashMap invariant broken: {e:#}");
        pool.get_or_connect(name).await
    }
    o => o,
}

Prevention

When it happens

Trigger: Unreachable in correct code: insert immediately followed by get_mut on the same key under an exclusive borrow cannot yield None.

Common situations: Not hit in practice; its appearance would indicate memory corruption, a custom HashMap with broken behavior, or a refactor that moved the insert behind a fallible operation.

Related errors


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