Hmbown/CodeWhale · warning · anyhow::Error
MCP catalog changed after tool resolution; retry the call
Error message
MCP catalog changed after tool resolution; retry the call
What it means
Every MCP connection carries a catalog_generation counter that the manager bumps whenever dynamic servers are registered/removed or a connection is refreshed/dropped (crates/tui/src/mcp.rs:2567,2618,3400,3410). call_tool resolves an advertised tool against a snapshot of that generation; if the live connection's generation differs by the time the call is about to execute, the resolved route may name a tool or authority that no longer exists, so the call is aborted and the caller must re-resolve before retrying.
Source
Thrown at crates/tui/src/mcp.rs:3314
let name = arguments
.get("name")
.and_then(|v| v.as_str())
.context("Missing 'name' argument")?;
let args = arguments
.get("arguments")
.cloned()
.unwrap_or(serde_json::json!({}));
return self.get_prompt(server_name, name, args).await;
}
let route = self.resolve_advertised_tool(prefixed_name).await?;
let server_name = route.server_name.clone();
let tool_name = route.tool_name.clone();
// Copy the global timeouts to avoid borrow conflict
let global_timeouts = self.config.timeouts;
let conn = self.get_or_connect(&server_name).await?;
if conn.catalog_generation != route.catalog_generation {
anyhow::bail!("MCP catalog changed after tool resolution; retry the call");
}
if conn
.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)
{
anyhow::bail!("MCP tool '{tool_name}' is disabled for server '{server_name}'");
}
let timeout = conn.config().effective_execute_timeout(&global_timeouts);
match conn.call_tool(&tool_name, arguments.clone(), timeout).await {
Ok(result) => Ok(result),
Err(err) if is_mcp_stale_session_error(&err) => {
tracing::debug!(
target: "mcp",View on GitHub (pinned to 8880682c63)
Solutions
- Retry the call from the top (re-resolve the tool name) with a small bounded number of attempts
- Serialize dynamic server registration/removal with tool invocation (single writer for catalog mutations)
- If it persists, trace catalog_generation bumps to find which code path keeps mutating the catalog mid-call
- Confirm the tool still appears in the server's refreshed tools/list before retrying
Example fix
// before
let result = manager.call_tool(prefixed_name, args).await?;
// after
let mut attempt = 0;
let result = loop {
attempt += 1;
match manager.call_tool(prefixed_name, args.clone()).await {
Ok(out) => break Ok(out),
Err(e) if e.to_string().contains("catalog changed after tool resolution") && attempt < 3 => continue,
Err(e) => break Err(e),
}
}?; Defensive patterns
Strategy: retry
Try / catch
let mut attempts = 0;
loop {
attempts += 1;
match manager.call_tool(name, args.clone()).await {
Ok(out) => return Ok(out),
Err(e) if e.to_string().contains("catalog changed after tool resolution") && attempts < 3 => continue,
Err(e) => return Err(e),
}
} Prevention
- Serialize dynamic MCP server add/remove with tool calls instead of mutating the catalog concurrently
- Treat this error as transient: always re-resolve the tool before retrying rather than resending blindly
- Bound retries (2-3 attempts) so a churn loop fails fast instead of spinning
When it happens
Trigger: Calling manager.call_tool while another task runs add_runtime_server_config/remove_runtime_server_config, refreshes a server catalog, or triggers drop_connection between resolve_advertised_tool and get_or_connect.
Common situations: An agent turn executing tools while the user adds/removes an MCP server in another pane; a background reconnect racing an in-flight tool call; a flaky server that keeps being dropped and re-added mid-call.
Related errors
- MCP OAuth setup cancelled after plugin authority changed
- MCP connection '{}' was cancelled
- MCP session preflight cancelled after plugin authority chang
- MCP Streamable HTTP session expired; retry with a new sessio
- --retry-delay-ms must be between 0 and 30000
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/b21264f53b4e4af0.
Report an issue: GitHub.