{"record":{"id":"9e8ad680a791d719","repo":"tinyhumansai/openhuman","slug":"stdio-mcp-server-closed-stdout-while-waiting-for","errorCode":null,"errorMessage":"stdio MCP server closed stdout while waiting for `{method}`","messagePattern":"stdio MCP server closed stdout while waiting for `(.+?)`","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/mcp/config_servers/stdio.rs","lineNumber":226,"sourceCode":"        method: &str,\n        params: Value,\n    ) -> anyhow::Result<Value> {\n        let id = self.next_id.fetch_add(1, Ordering::Relaxed);\n        let line = serde_json::to_string(&json!({\n            \"jsonrpc\": \"2.0\",\n            \"id\": id,\n            \"method\": method,\n            \"params\": params,\n        }))?;\n        session.stdin.write_all(line.as_bytes()).await?;\n        session.stdin.write_all(b\"\\n\").await?;\n        session.stdin.flush().await?;\n\n        loop {\n            let mut response = String::new();\n            let read = session.stdout.read_line(&mut response).await?;\n            if read == 0 {\n                anyhow::bail!(\"stdio MCP server closed stdout while waiting for `{method}`\");\n            }\n            let trimmed = response.trim();\n            if trimmed.is_empty() {\n                continue;\n            }\n            if !trimmed.starts_with('{') && !trimmed.starts_with('[') {\n                tracing::debug!(\n                    target: \"[mcp_client::stdio]\",\n                    command = %self.command,\n                    line = %trimmed,\n                    \"ignoring non-JSON stdout line from stdio MCP server\"\n                );\n                continue;\n            }\n            let payload: Value = serde_json::from_str(trimmed)\n                .with_context(|| format!(\"parsing stdio MCP response: {trimmed}\"))?;\n            if let Some(err) = payload.get(\"error\") {\n                anyhow::bail!(\"MCP stdio error: {err}\");","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/mcp/config_servers/stdio.rs#L208-L244","documentation":"During a stdio JSON-RPC round-trip, `read_line` on the server's stdout returned 0 bytes — the child process closed/exited before answering the pending `{method}` request. Because a single request/response is framed per line, EOF means the reply will never arrive, so the client bails immediately instead of waiting on a timeout. Typical root cause is the server crashing on startup (bad args, missing env, panicking on the request) after stdout closed.","triggerScenarios":"`initialize`, `tools/list`, or `tools/call` over stdio where the spawned command exits first: wrong CLI args making npx/uvx fail fast, the package failing to install, an unhandled exception in the server triggered by the request, or the server being OOM-killed.","commonSituations":"First `initialize` after a config change introduced a bad flag; npx package name typo (npx errors out and exits); server requires an env var/API key it reads at startup and aborts when missing; intermittent OOM or the user/system killing the helper process.","solutions":["Run the exact `command args` from the config manually in a terminal and watch stderr — the crash reason is almost always printed there.","Fix args/package names (e.g. missing `-y` for npx, wrong package version).","Supply required env vars (API keys) via the server entry's `env` config.","If it dies only under load, check memory limits; a server that dies on one specific tool call is a server bug — capture its stderr/stack."],"exampleFix":"# before — npx prompts/fails and exits immediately\n[[mcp_client.servers.context7]]\ncommand = \"npx\"\nargs = [\"@upstash/context7-mcp\"]\n\n# after — non-interactive install flag, plus debug by running it yourself:\n#   npx -y @upstash/context7-mcp\n[[mcp_client.servers.context7]]\ncommand = \"npx\"\nargs = [\"-y\", \"@upstash/context7-mcp\"]","handlingStrategy":"retry","validationCode":"null","typeGuard":null,"tryCatchPattern":"// on 'closed stdout while waiting for': tear down and re-initialize once\nmatch stdio_client.request(method, params).await {\n    Err(e) if e.to_string().contains(\"closed stdout\") => {\n        stdio_client = McpStdioClient::new(cmd, args, env, None, identity);\n        stdio_client.initialize().await?;\n        stdio_client.request(method, params).await\n    }\n    other => other,\n}","preventionTips":["Smoke-test `command args` in a terminal before enabling a stdio server config.","Pass required env/API keys via the server entry's env config so startup never aborts.","Use `-y`/non-interactive flags for npx/uvx so install prompts cannot kill the process."],"tags":["mcp","stdio","process-exit","eof","json-rpc"],"backgroundTag":"subprocess-exited-unexpectedly","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}