{"record":{"id":"1d914b846d0b0630","repo":"zeroclaw-labs/zeroclaw","slug":"mcp-server-server-name-timed-out-after-timeou-1d914b","errorCode":null,"errorMessage":"MCP server `{server_name}` timed out after {timeout_secs}s before writing {operation}","messagePattern":"MCP server `(.+?)` timed out after (.+?)s before writing (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/mcp_client.rs","lineNumber":666,"sourceCode":"                        WARN,\n                        ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Timeout)\n                            .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                            .with_attrs(::serde_json::json!({\n                                \"mcp_server\": &server_name,\n                                \"rpc_method\": rpc_method,\n                                \"timeout_secs\": timeout_secs,\n                                \"outcome_unknown\": unknown_epoch.is_some(),\n                            })),\n                        \"mcp_client: MCP request timed out\"\n                    );\n                    if let Some(epoch) = unknown_epoch {\n                        self.spawn_recovery(epoch, operation.to_string());\n                        bail!(\n                            \"MCP server `{server_name}` timed out after {timeout_secs}s during \\\n                             {operation}; outcome unknown and request was not replayed\"\n                        );\n                    }\n                    bail!(\n                        \"MCP server `{server_name}` timed out after {timeout_secs}s before writing \\\n                         {operation}\"\n                    );\n                }\n                Ok(Ok(response)) => {\n                    cancellation_guard.disarm();\n                    return Ok(response);\n                }\n                Ok(Err(error)) => {\n                    if let Some(epoch) = lifecycle.outcome_unknown_epoch() {\n                        cancellation_guard.disarm();\n                        self.spawn_recovery(epoch, operation.to_string());\n                        return Err(error).with_context(|| {\n                            format!(\n                                \"MCP server `{server_name}` failed during {operation}; outcome \\\n                                 unknown and request was not replayed\"\n                            )\n                        });","sourceCodeStart":648,"sourceCodeEnd":684,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/mcp_client.rs#L648-L684","documentation":"The overall timeout budget expired before the request was ever written — typically while waiting behind the serial gate, a pending recovery, or the epoch write gate. Because nothing was sent, this is the safe flavor of timeout: the operation definitively did not happen, so retrying cannot duplicate it.","triggerScenarios":"A prior request's recovery still in progress when the budget ran out; many concurrent calls serialized onto one server; a timeout so small that even lock acquisition exceeds it.","commonSituations":"Bursty fan-out to a single MCP server; a global timeout tuned for fast tools but applied while the server is stuck in recovery; undersized budgets under load.","solutions":["Retry immediately — the request never left the client, so there is no duplicate risk","Raise timeout_secs if legitimate queuing (recovery, serialization) regularly consumes the budget","Reduce or batch concurrent calls to the same server","If recovery is the recurring holdup, fix the underlying connection instability flagged in the logs"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"fn is_pre_write_timeout(err: &anyhow::Error) -> bool {\n    err.to_string().contains(\"before writing\") && err.to_string().contains(\"timed out\")\n}","tryCatchPattern":"Safe to retry immediately with the same arguments — the request never left the client. If it recurs, back off and raise the timeout instead of tight-looping.","preventionTips":["Size timeout_secs to include queuing behind recovery and serialization, not just the RPC itself","Batch or throttle concurrent calls to a single MCP server","Fix recurring recovery stalls so the budget is spent on the request, not the wait"],"tags":["mcp","timeout","serialization","recovery"],"backgroundTag":"request-timeout","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}