Hmbown/CodeWhale · error · anyhow::Error
; reconnect failed
Error message
{err:#}; reconnect failed: {reconnect_err:#} What it means
When a tool call fails because the connection dropped, the pool attempts a reconnect; if that reconnect also fails, this error chains both — the original call error first, then the reconnect failure — so neither diagnosis is swallowed.
Solutions
- Read the original error (the first segment) — fix that root cause first, since reconnect only failed as a consequence
- Verify the server can start at all (run its command manually, check auth/env)
- Restart the session or remove/re-add the server config to force a clean reconnect
Defensive patterns
Strategy: try-catch
Validate before calling
// verify the server is reachable before heavy tool use
if !conn.is_ready() { conn.reconnect().await?; } Try / catch
match call_tool(tool, args).await {
Err(call_err) => match reconnect().await {
Ok(fresh) => fresh.call_tool(tool, args).await,
Err(re) => Err(anyhow!("{call_err:#}; reconnect failed: {re:#}")),
},
ok => ok,
} Prevention
- Always preserve the original error when wrapping reconnect failures
- Monitor server process health to catch crashes before tool calls fail
- Test reconnect paths with servers that fail to start
When it happens
Trigger: conn.call_tool fails, the pool's reconnect path is entered, and the reconnect itself returns Err(reconnect_err); both messages are combined.
Common situations: MCP server process crashed mid-session and refuses to restart (bad command, port conflict, missing credentials); network drop with a broken config underneath.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- A managed, project or plugin connector already uses this…
- Absolute path should not warn
- active plugin registry is missing its pre-dotenv…
- app_denied
- {backoff.last_error}
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/218894d1d0fe9901.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp.rs:4972
.config()
.reviewed_plugin
.as_ref()
.map(|source| &source.authority)
!= route.plugin_authority.as_ref()
|| !conn.config().is_tool_enabled(&tool_name)
|| !conn.tools().iter().any(|tool| tool.name == tool_name)
{
Err(anyhow::anyhow!(
"MCP tool '{tool_name}' is disabled for server '{server_name}'"
))
} else {
let timeout = conn.config().effective_execute_timeout(&global_timeouts);
conn.call_tool(&tool_name, arguments, timeout).await
}
}
// A reconnect that fails must not swallow the call error
// that triggered it: report both, original first.
Err(reconnect_err) => Err(anyhow::anyhow!(
"{err:#}; reconnect failed: {reconnect_err:#}"
)),
}
}
Err(err) => Err(err),
};
// A credential the server stopped accepting mid-session (revoked or
// rotated elsewhere, refresh rejected) is the same `◆ auth required`
// class as a failed connect: land it in the same typed state so the
// next catalog offers the login tool instead of a dead error.
result.map_err(|err| self.note_live_call_failure(&server_name, err))
}
/// Get list of configured server names (static + dynamic)
#[allow(dead_code)] // Public API for MCP consumers
pub fn server_names(&self) -> Vec<String> {
let mut names: Vec<String> = self
.configView on GitHub (pinned to 73e0f67d83)