{"record":{"id":"d69aa2ada7ab21d9","repo":"Hmbown/CodeWhale","slug":"credential-handoff-could-not-write-to-stdout","errorCode":null,"errorMessage":"credential handoff could not write to stdout","messagePattern":"credential handoff could not write to stdout","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/cli/src/credential_handoff.rs","lineNumber":71,"sourceCode":"    resolved\n        .api_key\n        .filter(|value| !value.trim().is_empty())\n        .context(\"no usable runtime-effective API key\")\n}\n\npub(crate) fn handoff_secret_line(\n    writer: &mut impl Write,\n    stdout_is_terminal: bool,\n    resolve: impl FnOnce() -> Result<String>,\n) -> Result<()> {\n    prepare_stdout(stdout_is_terminal)?;\n    let secret = Zeroizing::new(resolve().map_err(|_| anyhow::anyhow!(\"unavailable credential\"))?);\n    ensure!(!secret.trim().is_empty(), \"credential handoff was empty\");\n    let written = writeln!(writer, \"{}\", secret.as_str());\n    if written.is_ok() || written.is_err_and(|error| error.kind() == ErrorKind::BrokenPipe) {\n        return Ok(());\n    }\n    bail!(\"credential handoff could not write to stdout\")\n}\n#[cfg(test)]\nmod tests;\n","sourceCodeStart":53,"sourceCodeEnd":75,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/cli/src/credential_handoff.rs#L53-L75","documentation":"handoff_secret_line writes the resolved secret (as a single line) to the given stdout writer. A write failure that is not BrokenPipe (e.g. EIO, EBADF, disk-full on the pipe target) is a real error and is bailed with this generic message that deliberately omits OS details and the secret value, keeping the credential out of logs and error output.","triggerScenarios":"writeln! to the stdout writer fails with an error kind other than BrokenPipe during `credential api-key`/handoff; raised in handoff_secret_line for the pipe/terminal consumers covered by its tests.","commonSituations":"Writing into a full or closed pseudo-terminal; redirecting handoff output to a file on a full disk; output fd closed or invalid in a sandboxed/containerized runner; downstream process died in a way that surfaces EIO rather than EPIPE.","solutions":["Re-run the handoff with a healthy stdout: a normal terminal or `| consumer` (BrokenPipe is already treated as a clean settlement).","Check disk space and fd validity where stdout is redirected (`df`, `ls -l /proc/<pid>/fd/1`).","Inside containers/CI, ensure the pipe consumer stays alive long enough to read, and inspect the generic error at the OS level — the message intentionally hides errno details."],"exampleFix":"// before\nconsumer --exiting-immediately < <(codewhale credential api-key xai)\n// after\ncodewhale credential api-key xai > /tmp/keyline\nconsumer < /tmp/keyline","handlingStrategy":"try-catch","validationCode":"if let Err(e) = writeln!(std::io::stdout(), \"probe\") {\n    if e.kind() != std::io::ErrorKind::BrokenPipe {\n        eprintln!(\"stdout is not writable: {}\", e.kind());\n    }\n}\n","typeGuard":null,"tryCatchPattern":"match run_handoff(...) {\n    Err(e) if e.to_string() == \"credential handoff could not write to stdout\" => {\n        eprintln!(\"check stdout target (disk space, closed fd) — error details are redacted by design\");\n    }\n    other => other,\n}","preventionTips":["Keep the pipe consumer alive until it has read the line (BrokenPipe is handled, other errors are not).","Avoid redirecting handoff output to files on full filesystems.","Treat the message as intentionally detail-free: never log the secret; debug via the consumer's exit code."],"tags":["credentials","stdout","io","pipe","security"],"backgroundTag":"file-write-failed","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"}