Hmbown/CodeWhale · critical · anyhow::Error
MCP connection task panicked
Error message
MCP connection task panicked
What it means
MCP connection tasks are spawned with catch_unwind; if the underlying connect future panics, the panic is converted into a regular Err so the pool records it as a connection failure instead of tearing down the caller. The message intentionally hides panic details behind a stable label.
Solutions
- Inspect logs/stderr for the original panic backtrace (panics usually print before being caught)
- Identify the server whose connect panicked and reproduce its handshake in isolation
- File/fix the panic in the connector or transport code — this message indicates an internal bug, not a config problem
Defensive patterns
Strategy: try-catch
Try / catch
let result = std::panic::catch_unwind(|| connector.connect())
.unwrap_or_else(|_| Err(anyhow!("MCP connection task panicked")));
if let Err(e) = result { log::error!("mcp connect panic: {e:#}"); } Prevention
- Avoid unwrap/expect in transport and connector code
- Fuzz or test connectors against malformed server responses
- Keep panics logged with backtraces so the panic source is diagnosable
When it happens
Trigger: A panic inside the per-server connect task (e.g. unwrap/ indexing bug in a connector or transport) while handshaking an MCP server.
Common situations: Bug in an MCP transport/SDK integration triggered by a malformed server response; a custom plugin connector panicking during handshake.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- connection stdin poisoned by an earlier panic
- Expected Ok for scoped npm package via
- Expected Ok, got
- Expected Ok when cwd anchors relative path, got
- Expected Warning for relative path argument, got
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/f3f5f533c288203b.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp.rs:3801
let network_policy = network_policy.clone();
joins.spawn(async move {
let connection = std::panic::AssertUnwindSafe(async {
let _permit = permit.acquire_owned().await;
McpConnection::connect_with_policy(
name.clone(),
config,
&timeouts,
network_policy.as_ref(),
)
.await
.map(|mut connection| {
connection.catalog_generation = catalog_generation;
connection
})
})
.catch_unwind()
.await
.unwrap_or_else(|_| Err(anyhow::anyhow!("MCP connection task panicked")));
(name, connection)
});
}
joins
}
/// Connect to all enabled servers, returning errors for failed connections.
///
/// Servers connect **concurrently** (bounded by [`Self::CONNECT_CONCURRENCY`]).
/// This used to be a sequential loop over `get_or_connect`, so every
/// server paid the slowest server's spawn+handshake from its own budget:
/// with the default 10s connect timeout, N servers meant a worst case of
/// N×10s before the pool was usable. Each connection still gets its own
/// configured connect timeout; one wedged server can no longer serialize
/// the rest.
///
/// Semantics preserved from the sequential loop: only configured serversView on GitHub (pinned to 73e0f67d83)