Hmbown/CodeWhale · warning · anyhow::Error
MCP connection '{}' was cancelled
Error message
MCP connection '{}' was cancelled What it means
Raised by McpConnection::send when the connection-wide CancellationToken fires while a JSON-RPC request is still being written to the transport. The tokio::select! is biased so cancellation always wins over the pending transport.send, and the connection state is set to Disconnected before the error is returned. It is a cooperative shutdown signal, not a transport failure: some other component (close(), a config reload dropping the connection, a workspace switch, or plugin-authority revocation) decided this connection must stop.
Source
Thrown at crates/tui/src/mcp.rs:2174
}
/// Get connection state
#[allow(dead_code)] // Public API for MCP consumers
pub fn state(&self) -> ConnectionState {
self.state
}
fn next_id(&self) -> String {
self.request_id.fetch_add(1, Ordering::SeqCst).to_string()
}
async fn send(&mut self, msg: serde_json::Value) -> Result<()> {
let bytes = serde_json::to_vec(&msg).context("Failed to serialize MCP JSON-RPC message")?;
tokio::select! {
biased;
_ = self.cancel_token.cancelled() => {
self.state = ConnectionState::Disconnected;
anyhow::bail!("MCP connection '{}' was cancelled", self.name)
}
result = self.transport.send(bytes) => result,
}
}
async fn recv(&mut self, expected_id: String) -> Result<serde_json::Value> {
loop {
let bytes = match tokio::time::timeout(
Duration::from_secs(self.read_timeout_secs),
async {
tokio::select! {
biased;
_ = self.cancel_token.cancelled() => {
anyhow::bail!("MCP connection '{}' was cancelled", self.name)
}
result = self.transport.recv() => result,
}
},View on GitHub (pinned to 8880682c63)
Solutions
- Treat as a transient, expected shutdown error: drop the stale McpConnection reference and retry the operation through McpPool::get_or_connect, which transparently reconnects.
- If it happens mid-turn during normal use, check whether the MCP config file changed on disk (mtime-triggered reload) and let the reconnect path rebuild the connection.
- Avoid holding a cached &mut McpConnection across await points that can span a config reload; always re-fetch via the pool.
- If you intentionally cancelled (close()), simply propagate or ignore the error — do not retry.
Example fix
// before
let conn = pool.get_or_connect("github").await?;
let tools = conn.list_tools().await?; // fails with 'was cancelled' after reload
// after
let tools = match pool.get_or_connect("github").await?.list_tools().await {
Ok(tools) => tools,
Err(e) if e.to_string().contains("was cancelled") => {
pool.get_or_connect("github").await?.list_tools().await?
}
Err(e) => return Err(e),
}; Defensive patterns
Strategy: retry
Try / catch
// Rust: match on the error, distinguish cancellation from real failures
match conn.send_request(...).await {
Err(e) if e.to_string().contains("was cancelled") => {
// connection torn down (reload/close/authority): rebuild it
let conn = pool.get_or_connect(server).await?;
conn.send_request(...).await
}
other => other,
} Prevention
- Always acquire connections through McpPool::get_or_connect instead of caching McpConnection handles across turns.
- Never retry when your own code called close() — cancellation is the intended outcome.
- Treat 'was cancelled' as a transient signal distinct from timeouts and protocol errors.
When it happens
Trigger: Calling any MCP method (tools/list, initialize, call_tool) on a connection whose cancel_token has been cancelled — e.g. after McpConnection::close(), after the pool drops the connection during reload_from_config_sources, or during switch_workspace_config_source_and_connect_all. Any in-flight request that reaches send() after cancellation gets this error.
Common situations: User edits .mcp config while a tool call is in flight (lazy auto-reload drops the connection); a reviewed plugin's catalog changes and the old connection is torn down; a sub-agent finishes and the shared pool is closed; tests cancel a connection to exercise shutdown paths.
Related errors
- MCP OAuth setup cancelled after plugin authority changed
- MCP session preflight cancelled after plugin authority chang
- invalid JSON payload at key {MCP_SERVER_DEFINITIONS_KEY}; co
- invalid MCP server definition list in key {MCP_SERVER_DEFINI
- Failed to render MCP template JSON: {e}
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/e360511cb918d6d3.
Report an issue: GitHub.