Hmbown/CodeWhale · error

Failed to write review payload

Error message

Failed to write review payload: {e}

What it means

This error is raised while posting a PR review via the GitHub CLI: the code spawns `gh api` and writes the JSON review payload to its stdin. If `stdin.write_all` fails (the child process has exited or its pipe is broken), it wraps the io::Error into this anyhow error. It means the review request never reached `gh`, so nothing was posted.

Solutions

  1. Run `gh auth status` and re-authenticate with `gh auth login` so the spawned `gh api` process doesn't exit before reading stdin
  2. Verify `which gh` points to the real GitHub CLI, not a wrapper that ignores stdin
  3. Retry the post; transient EPIPE on a freshly spawned child often resolves on a second attempt
  4. Upgrade gh (`gh upgrade` or package manager) — older versions had stdin-handling issues with `gh api`

Example fix

// before
let child = Command::new("gh").arg("api")...spawn()?;
// after
let status = Command::new("gh").arg("auth").arg("status").status()?;
if !status.success() { bail!("gh is not authenticated; run gh auth login"); }
let child = Command::new("gh").arg("api")...spawn()?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust
if std::process::Command::new("gh").arg("auth").arg("status").status().map(|s| s.success()) != Ok(true) {
    bail!("gh CLI is missing or unauthenticated; run gh auth login");
}

Try / catch

// Rust
match post_review(&payload) {
    Ok(()) => info!("review posted"),
    Err(e) if e.to_string().contains("Failed to write review payload") => {
        warn!("gh exited before reading stdin: {e:#}; check gh auth and retry");
        *publication = ReviewPublication::Uncertain;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: `stdin.write_all(serde_json::to_string(&payload)?.as_bytes())` fails while posting a review through `gh api POST repos/{repo}/pulls/{number}/reviews` — typically because the `gh` process exited before consuming stdin (e.g. bad auth made it exit immediately) or the pipe broke.

Common situations: `gh` not authenticated or removed/upgraded mid-call so it exits early and closes stdin; a wrapper/shim on PATH for `gh` that doesn't read stdin; JSON payload large enough to fill the pipe buffer after early child exit (broken pipe / EPIPE); sandboxed environments that kill spawned processes.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/91c270579f0766c8. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/lib.rs:9734

        .ok_or_else(|| anyhow::anyhow!("gh not found on PATH"))?;
    cmd.arg("api")
        .arg("--method")
        .arg("POST")
        .arg(format!("repos/{repo}/pulls/{number}/reviews"))
        .arg("--input")
        .arg("-")
        .stdin(std::process::Stdio::piped())
        .stdout(std::process::Stdio::piped())
        .stderr(std::process::Stdio::piped());
    let mut child = cmd
        .spawn()
        .map_err(|e| anyhow::anyhow!("Failed to run `gh api`: {e}"))?;
    *publication = ReviewPublication::Uncertain;
    if let Some(stdin) = child.stdin.as_mut() {
        use std::io::Write;
        stdin
            .write_all(serde_json::to_string(&payload)?.as_bytes())
            .map_err(|e| anyhow::anyhow!("Failed to write review payload: {e}"))?;
    }
    let output = child
        .wait_with_output()
        .map_err(|e| anyhow::anyhow!("Failed to wait for `gh api`: {e}"))?;
    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
        bail!("gh api POST repos/{repo}/pulls/{number}/reviews failed: {stderr}");
    }
    *publication = ReviewPublication::Posted;
    Ok(())
}

/// Publish a completed PR review exactly once: resolve the repository, render
/// the summary, and include every comment whose position the diff confirms.
/// A failed request can have an uncertain remote outcome, so reconciliation
/// and any retry stay under the caller's control rather than risking a duplicate.
fn post_pr_review(
    number: u32,

View on GitHub (pinned to 73e0f67d83)