Hmbown/CodeWhale · error
Stdio transport closed {stderr}
Error message
Stdio transport closed
{stderr} What it means
The MCP server's stdout hit EOF (the child exited) and the stderr tail ring buffer had content, which is appended so the exit reason is visible. This is the classic 'MCP server failed at startup' error: the process was spawned, then terminated while printing a message to stderr.
Source
Thrown at crates/tui/src/mcp/stdio.rs:325
let mut line_bytes: Vec<u8> = Vec::new();
loop {
// Bounded read: a server emitting a newline-free multi-GB "line"
// must not OOM us (read_line is unbounded).
let bytes =
match read_line_capped(&mut self.reader, &mut line_bytes, MAX_MCP_RESPONSE_BYTES)
.await
{
Ok(b) => b,
Err(err) => {
if let Some(stderr) = format_stderr_context(&self.stderr_tail).await {
anyhow::bail!("Stdio transport read error: {err}\n{stderr}");
}
return Err(err.into());
}
};
if bytes == 0 {
if let Some(stderr) = format_stderr_context(&self.stderr_tail).await {
anyhow::bail!("Stdio transport closed\n{stderr}");
}
anyhow::bail!("Stdio transport closed");
}
let line = String::from_utf8_lossy(&line_bytes);
let trimmed = line.trim();
if trimmed.is_empty() {
continue;
}
return Ok(trimmed.as_bytes().to_vec());
}
}
/// Send SIGTERM and wait up to `STDIO_SHUTDOWN_GRACE` for graceful exit,
/// then force termination and reap the child as the backstop.
async fn shutdown(&mut self) {
let mut child = self.child.lock().await;View on GitHub (pinned to 8880682c63)
Solutions
- Read the stderr excerpt in the message — it is the server's own failure output
- Copy the command and args from the config and run them manually; fix until the server starts and stays up
- Ensure the runtime (node, python, uvx, docker) is installed and visible on the app's PATH
- Declare any required env vars (API keys) in the server configuration
Example fix
// before: server exits with 'missing required argument: PATH' on stderr command: npx -y @modelcontextprotocol/server-filesystem // after: required argument supplied command: npx -y @modelcontextprotocol/server-filesystem /home/me/projects
Defensive patterns
Strategy: try-catch
Type guard
```rust
fn exited_with_stderr(msg: &str) -> bool {
msg.starts_with("Stdio transport closed\n")
}
``` Try / catch
```rust
match stdio_transport.recv().await {
Err(e) if exited_with_stderr(&format!("{e:#}")) => {
surface_stderr_tail(&e); // startup failure: show the server's reason
Err(e)
}
other => other.map(|f| f)?,
}
``` Prevention
- Validate command/args by running them manually before adding the server
- Declare required env vars (API keys) in the server entry
- Pin package versions so CLI flag changes don't silently break startup
When it happens
Trigger: The configured command exits immediately after spawn: bad or missing flags, a required env var (API key) unset, missing interpreter (node/npx/python not on PATH), or a package version whose CLI changed.
Common situations: Wrong command/args in the MCP server entry; missing required arguments (server prints usage and exits); node not installed for npx-based servers; breaking CLI changes after a package upgrade.
Related errors
- Stdio transport read error: {err} {stderr}
- Stdio transport closed
- -32603
- required MCP server failed to initialize
- ${name} is required
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/92ca0b6dbfb9a1a4.
Report an issue: GitHub.