{"record":{"id":"953d9fe36c437e5b","repo":"Hmbown/CodeWhale","slug":"stdio-transport-closed-exit-stderr","errorCode":null,"errorMessage":"Stdio transport closed{exit}\n{stderr}","messagePattern":"Stdio transport closed(.+?)\n(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp/stdio.rs","lineNumber":343,"sourceCode":"            {\n                Ok(b) => b,\n                Err(err) => {\n                    if let Some(stderr) = format_stderr_context(&self.stderr_tail).await {\n                        anyhow::bail!(\"Stdio transport read error: {err}\\n{stderr}\");\n                    }\n                    return Err(err.into());\n                }\n            };\n            if bytes == 0 {\n                // Let the stderr drain task catch up before snapshotting, and\n                // name the exit status: a reviewed plugin's stderr is never\n                // retained, so the status is the only reason the operator\n                // gets when the child dies before the handshake (#5916).\n                tokio::task::yield_now().await;\n                let exit = self.child.lock().await.try_wait().ok().flatten();\n                let exit = exit.map_or_else(String::new, |status| format!(\" ({status})\"));\n                if let Some(stderr) = format_stderr_context(&self.stderr_tail).await {\n                    anyhow::bail!(\"Stdio transport closed{exit}\\n{stderr}\");\n                }\n                anyhow::bail!(\"Stdio transport closed{exit}\");\n            }\n\n            let line_bytes = std::mem::take(&mut self.pending_line);\n            let line = String::from_utf8_lossy(&line_bytes);\n            let trimmed = line.trim();\n            if trimmed.is_empty() {\n                continue;\n            }\n\n            return Ok(trimmed.as_bytes().to_vec());\n        }\n    }\n\n    /// Send SIGTERM and wait up to `STDIO_SHUTDOWN_GRACE` for graceful exit,\n    /// then force termination and reap the child as the backstop.\n    async fn shutdown(&mut self) {","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/mcp/stdio.rs#L325-L361","documentation":"Thrown by the stdio MCP transport's `recv` when the child process's stdout reaches EOF (0 bytes read) — the server closed its output — and captured stderr is available. The message includes the child's exit status (via `try_wait`) and the stderr tail, which per issue #5916 is the only diagnostic when the child dies before the MCP handshake completes.","triggerScenarios":"`recv` reads 0 bytes from the child's stdout, yields once to let the stderr drain task catch up, snapshots the child's exit status, and finds non-empty stderr in `stderr_tail`.","commonSituations":"The MCP server command doesn't exist or fails to launch (module not found, bad interpreter); the child crashes on startup due to a config or env problem; the server exits after an unhandled error during a session.","solutions":["Read the stderr excerpt and exit status in the message — it names the child's startup or runtime failure.","Verify the configured command/args run standalone in a terminal and exit 0 on a trivial request.","Fix missing dependencies, wrong binary paths, or missing env vars the child needs.","Restart the MCP server / reopen the transport once the underlying cause is fixed."],"exampleFix":"// before: child exits immediately\n\"command\": \"python3\", \"args\": [\"mcp_server.py\"]  // stderr: No module named 'mcp'\n// after\n\"command\": \"python3\", \"args\": [\"-m\", \"pip\", \"install\", \"mcp\"], // then relaunch\n\"command\": \"python3\", \"args\": [\"mcp_server.py\"]","handlingStrategy":"validation","validationCode":"// Verify the command exists and starts before opening the transport\nwhich::which(&config.command)?;\nlet probe = std::process::Command::new(&config.command).args(&config.args).stdin(std::process::Stdio::piped()).stdout(std::process::Stdio::piped()).stderr(std::process::Stdio::piped()).spawn()?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run the MCP server command manually first; confirm it survives a trivial initialize handshake.","Ensure all env vars, paths, and dependencies the child needs are set in the transport config.","Read the stderr tail in the error before guessing at fixes."],"tags":["mcp","stdio","child-process","crash"],"backgroundTag":"connection-refused","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}