{"record":{"id":"b7f98ab61e77bb48","repo":"Hmbown/CodeWhale","slug":"mcp-server-server-invalid-child-stdout-while-awaiting-method","errorCode":null,"errorMessage":"MCP server '{server}': invalid child stdout while awaiting {method}: {error}","messagePattern":"MCP server '(.+?)': invalid child stdout while awaiting (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mcp/src/stdio_client.rs","lineNumber":893,"sourceCode":"                bail!(\n                    \"MCP server '{server}': process closed stdin before answering {method}{}\",\n                    self.exit_note()\n                );\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(ChildStdoutMessage::Line(line)) => line,\n                Ok(ChildStdoutMessage::Invalid(error)) => {\n                    bail!(\n                        \"MCP server '{server}': invalid child stdout while awaiting {method}: {error}\"\n                    );\n                }\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. They\n            // are skipped; only the matching response ends the wait. Valid\n            // requests and notifications are handled by the reader before\n            // they reach this rendezvous.","sourceCodeStart":875,"sourceCodeEnd":911,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/mcp/src/stdio_client.rs#L875-L911","documentation":"The reader thread parses each line of the child's stdout as JSON; unparseable lines are forwarded as ChildStdoutMessage::Invalid. When request() receives such an invalid-stdout notification while awaiting a response, it fails immediately with the underlying parse error rather than waiting out the timeout.","triggerScenarios":"The MCP server prints non-JSON text (debug logs, banners, panics) to stdout while a request is in flight.","commonSituations":"Servers using print/println debugging on stdout (stdout is the protocol channel); wrappers or tools emitting progress banners; a panic message interleaved with protocol output.","solutions":["Configure the server to log to stderr instead of stdout; fix any stray print statements.","Inspect the embedded `{error}` in the message to see the exact malformed line.","Remove wrapper scripts that print banners to stdout.","Verify only newline-delimited JSON-RPC messages are written to the child's stdout."],"exampleFix":"// server before\nprint(\"loading model...\")   // goes to stdout, corrupts protocol\n// after\neprintln!(\"loading model...\")  // stderr is free-form","handlingStrategy":"validation","validationCode":"// during development, wrap the server so stdout is protocol-only:\n// run `my-server 2>server.log` and check server.log for stray prints\nfn stdout_is_clean(log: &str) -> bool {\n    log.lines().all(|l| serde_json::from_str::<serde_json::Value>(l).is_ok())\n}","typeGuard":null,"tryCatchPattern":"match client.request(method, params, timeout) {\n    Ok(v) => v,\n    Err(e) if e.to_string().contains(\"invalid child stdout\") => {\n        eprintln!(\"server printed non-JSON to stdout: {e}\");\n        Err(e)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Configure all server logging to stderr; stdout is protocol-only in stdio MCP.","Grep server code for print/println/console.log writing to stdout.","Test new servers by capturing stdout and validating each line parses as JSON.","Keep banner/progress output off stdout in wrapper scripts."],"tags":["mcp","stdout","json-parse","stdio"],"backgroundTag":"invalid-json-response","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"}