{"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":"exception","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/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/cli/src/credential_handoff.rs#L53-L75","documentation":"credential_handoff writes a resolved secret as a single line to a writer (stdout) so a parent process can capture it. Broken pipes are tolerated - if the reader exits early (e.g. `head`), that counts as success. This bail fires only when the write fails with a different IO error: closed descriptor, EIO, ENOSPC, or a device error, meaning the secret may not have been delivered.","triggerScenarios":"The consumer process closes stdout in a way that surfaces as EBADF/EIO rather than BrokenPipe; the target disk/tmpfs is full when stdout is a file or a process-substitution redirect; stdout was already closed by daemonization before the handoff call.","commonSituations":"Wrapper scripts using process substitution that close FDs early; running under a supervisor that closes inherited stdout; disk-full CI runners when redirecting to a log; double-close of a pipe FD in the parent.","solutions":["Ensure the writer's stdout is an open pipe or file with a live consumer for the whole handoff","Free disk space when redirecting output to a file on a full filesystem","Let the reader keep stdout open until the secret line plus newline is consumed; only an early-exit BrokenPipe is OK","Check the exit path of the consuming process and avoid closing inherited FDs before the child writes"],"exampleFix":"# before\ncodewhale credential-handoff ... > /full/disk/out.txt\n# after\ncodewhale credential-handoff ... | consumer   # consumer reads the line and stays alive until EOF","handlingStrategy":"try-catch","validationCode":"use std::io::{ErrorKind, Write};\n\nfn write_secret_line(w: &mut impl Write, secret: &str) -> std::io::Result<()> {\n    match writeln!(w, \"{secret}\") {\n        Ok(()) => Ok(()),\n        Err(e) if e.kind() == ErrorKind::BrokenPipe => Ok(()), // reader done early: fine\n        Err(e) => Err(e),\n    }\n}","typeGuard":null,"tryCatchPattern":"if let Err(error) = write_secret_line(&mut stdout, &secret) {\n    if error.kind() != std::io::ErrorKind::BrokenPipe {\n        anyhow::bail!(\"credential handoff could not write to stdout: {error}\");\n    }\n}","preventionTips":["Keep the reading process alive until it consumes the secret line and EOF","Treat BrokenPipe as success; classify all other IO errors as delivery failure","Do not close inherited stdout FDs in supervisors before handoff completes","Check disk space when redirecting handoff output to files"],"tags":["credentials","stdout","io","ipc","broken-pipe"],"backgroundTag":"stdout-write-failed","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}