{"record":{"id":"6797c34c90249b20","repo":"openai/codex","slug":"mcp-http-headers-helper-stdout-was-unavailable","errorCode":null,"errorMessage":"MCP HTTP headers helper stdout was unavailable","messagePattern":"MCP HTTP headers helper stdout was unavailable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"codex-rs/rmcp-client/src/http_headers.rs","lineNumber":325,"sourceCode":"    #[cfg(not(windows))]\n    let child = process\n        .spawn()\n        .map_err(|error| anyhow!(\"MCP HTTP headers helper failed to start: {error}\"))?;\n    let mut process = HelperProcess {\n        #[cfg(unix)]\n        process_group_id: child\n            .id()\n            .ok_or_else(|| anyhow!(\"MCP HTTP headers helper process id was unavailable\"))?,\n        child,\n        #[cfg(windows)]\n        job,\n    };\n    let output = tokio::time::timeout(HELPER_TIMEOUT, async {\n        let stdout = process\n            .child\n            .stdout\n            .take()\n            .ok_or_else(|| anyhow!(\"MCP HTTP headers helper stdout was unavailable\"))?;\n        let mut output = Vec::new();\n        stdout\n            .take((MAX_HELPER_OUTPUT_BYTES + 1) as u64)\n            .read_to_end(&mut output)\n            .await?;\n        if output.len() > MAX_HELPER_OUTPUT_BYTES {\n            return Err(anyhow!(\"MCP HTTP headers helper output exceeds 64 KiB\"));\n        }\n        let status = process.child.wait().await?;\n        if !status.success() {\n            return Err(anyhow!(\n                \"MCP HTTP headers helper exited with status {status}\"\n            ));\n        }\n        Ok(output)\n    })\n    .await\n    .map_err(|_| anyhow!(\"MCP HTTP headers helper timed out after 10 seconds\"))??;","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/rmcp-client/src/http_headers.rs#L307-L343","documentation":"run_helper() takes child.stdout to read the JSON output; take() returns None only if stdout was not piped or was already consumed. The command is always built with .stdout(Stdio::piped()), so in practice this error signals a broken internal invariant — the spawn configuration was changed or stdout was taken before the read — rather than anything a user can configure.","triggerScenarios":"Internal only: piped stdout missing on the spawned helper because Stdio::piped() was removed or stdout was taken elsewhere before the read task.","commonSituations":"Only after modifying rmcp-client spawn code or a custom fork; not reachable through normal MCP server configuration.","solutions":["Keep Stdio::piped() for helper stdout in the spawn configuration","Do not take() child.stdout before the read future runs","If seen with a stock build, report it as an internal bug upstream"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"fn is_helper_stdout_unavailable(error: &anyhow::Error) -> bool {\n    error.to_string().contains(\"stdout was unavailable\")\n}","tryCatchPattern":"// internal invariant: report rather than retry\nif let Err(error) = provider.headers().await {\n    if error.to_string().contains(\"stdout was unavailable\") {\n        // stock builds should never hit this; file an upstream bug\n    }\n}","preventionTips":["Keep Stdio::piped() on helper stdout in any fork","Never take() child.stdout before the read future","Cover helper spawn in integration tests on all platforms"],"tags":["rust","mcp","internal-invariant","stdout","headers-helper"],"backgroundTag":"process-stdout-missing","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}