{"record":{"id":"16c89651277f9418","repo":"Hmbown/CodeWhale","slug":"stdio-transport-closed-exit-n-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":354,"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":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp/stdio.rs#L336-L372","documentation":"The stdio MCP transport detected EOF on the child's stdout and the child process has closed. The error reports the exit status (when retrievable) and appends the captured stderr tail so the operator learns why the child died — critical when the child dies before the MCP handshake completes, since the stderr tail is otherwise not retained.","triggerScenarios":"recv() reads zero bytes (EOF) from the child's stdout and stderr context is available — child exited normally or crashed after startup, e.g. bad arguments, missing dependency, failed MCP initialize handshake.","commonSituations":"Typo'd command or wrong path in MCP server config so the child exits immediately; server crashes on startup due to bad env or missing API key; child killed by a signal (shown in the exit status).","solutions":["Inspect the stderr tail and exit status embedded in the message.","Run the configured MCP server command manually in a shell to reproduce the startup failure.","Fix the command, args, or environment in the MCP server config.","Check for signal kills (SIGKILL/SIGSEGV in the exit status) and address the underlying cause."],"exampleFix":"// before: wrong command in MCP config\ncommand = \"npx\"\nargs = [\"mcp-server-filles\"]  // typo -> child exits, EOF\n\n// after\ncommand = \"npx\"\nargs = [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match transport.recv().await {\n    Err(e) if e.to_string().starts_with(\"Stdio transport closed\") => {\n        // message carries exit status + stderr tail; fail fast with that context\n        return Err(anyhow!(\"MCP server died before handshake: {e}\"));\n    }\n    other => other?,\n}","preventionTips":["Smoke-test the exact configured command line in a shell before adding it to MCP config.","Check MCP server env requirements (API keys, paths) at startup.","Pin working versions of MCP server packages to avoid surprise startup breakage."],"tags":["mcp","stdio","process-exit","subprocess"],"backgroundTag":"connection-refused","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}