{"record":{"id":"8fa53522f91b130b","repo":"Hmbown/CodeWhale","slug":"mcp-server-server-process-closed-stdout-befor","errorCode":null,"errorMessage":"MCP server '{server}': process closed stdout before answering {method}{}","messagePattern":"MCP server '(.+?)': process closed stdout before answering (.+?)(.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/mcp/src/stdio_client.rs","lineNumber":361,"sourceCode":"                );\n            }\n            return Err(err)\n                .with_context(|| format!(\"MCP server '{server}': failed to send {method}\"));\n        }\n\n        let deadline = Instant::now() + timeout;\n        loop {\n            let remaining = deadline.saturating_duration_since(Instant::now());\n            if remaining.is_zero() {\n                bail!(\"MCP server '{server}': {method} timed out after {timeout:?}\");\n            }\n            let line = match self.responses.recv_timeout(remaining) {\n                Ok(line) => line,\n                Err(RecvTimeoutError::Timeout) => {\n                    bail!(\"MCP server '{server}': {method} timed out after {timeout:?}\");\n                }\n                Err(RecvTimeoutError::Disconnected) => {\n                    bail!(\n                        \"MCP server '{server}': process closed stdout before answering {method}{}\",\n                        self.exit_note()\n                    );\n                }\n            };\n\n            // Servers occasionally emit banners or log lines on stdout, and\n            // notifications carry no id. Both are skipped; only the matching\n            // response ends the wait.\n            let Ok(message) = serde_json::from_str::<Value>(&line) else {\n                continue;\n            };\n            if message.get(\"id\").and_then(Value::as_u64) != Some(id) {\n                continue;\n            }\n            if let Some(error) = message.get(\"error\") {\n                bail!(\"MCP server '{server}': {method} failed: {error}\");\n            }","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/mcp/src/stdio_client.rs#L343-L379","documentation":"The reader thread that drains the MCP server's stdout hit EOF, disconnecting the response channel while a request was outstanding: the child process closed stdout (died) before answering. exit_note() appends the reaped exit status where available. On Linux this is the usual symptom of a server that dies mid-request; on macOS the EPIPE/stdin variant tends to win the race — both describe the same death.","triggerScenarios":"Server process crashes while executing a tools/call (panic, OOM kill, segfault); server calls exit() after an internal error; wrapper scripts (npx, docker) exiting and closing stdout; server killed externally (OOM reaper, container stop) during a long request.","commonSituations":"Memory-hungry MCP servers killed by the OOM killer mid-task; node servers dying on unhandled promise rejections; containers wrapped as MCP servers being stopped; servers with a fixed idle timeout that exit during long operations.","solutions":["Read the exit status appended to the message, then the server's stderr (inherited) for its crash reason","Fix the server-side crash: capture its panic/exit cause by running the same command and request manually","If the server is killed for memory, raise the limit or make the tool stream/paginate instead of buffering","Restart the server (re-register via register_server) and retry idempotent requests; never blindly retry a tools/call that may have had side effects"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"null","typeGuard":null,"tryCatchPattern":"match client_call(&client, tool, args) {\n    Ok(v) => Ok(v),\n    Err(err) if err.to_string().contains(\"closed stdout\") => {\n        // server died mid-request: do NOT auto-retry non-idempotent tools\n        Err(err).context(\"MCP server died mid-request; check its stderr/exit status before retrying\"))\n    }\n    Err(err) => Err(err),\n}","preventionTips":["Only retry after a mid-request death when the tool is idempotent — the first call may have had side effects","Monitor the server process (memory, OOM events) for recurring mid-flight deaths","Re-register a fresh client (register_server) after any death before retrying"],"tags":["mcp","stdio","process-exit","eof","jsonrpc"],"backgroundTag":"server-process-exited","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}