Hmbown/CodeWhale · error

gh pr view did not return exact base and head commit IDs

Error message

gh pr view did not return exact base and head commit IDs

What it means

view_with shells out to `gh pr view --json <fields>` and deserializes the JSON into GhPullRequest. After parsing it validates that base_sha and head_sha are exact 40-char commit IDs via commit_id(); if either is missing, abbreviated, or otherwise not an exact SHA, the review tool refuses the PR because all later diff/receipt logic pins to exact base/head commits.

Solutions

  1. Run `gh pr view <number> --json baseRefOid,headRefOid` manually and confirm both fields contain full 40-character SHAs
  2. Upgrade or repair the gh CLI to a version that returns exact oids (`gh --version`); remove any wrapper script shadowing gh in PATH
  3. Re-push the PR branch so headRefOid resolves, and ensure the base branch still exists on the remote

Example fix

// before: trusting whatever gh returned
let view: GhPullRequest = serde_json::from_str(&run(Program::Gh, &args)?)?;
// after: fail fast with a clear message when oids are missing
let view: GhPullRequest = serde_json::from_str(&run(Program::Gh, &args)?)
    .context("gh pr view returned incomplete PR metadata")?;
if !commit_id(&view.base_sha) || !commit_id(&view.head_sha) {
    bail!("gh pr view did not return exact base and head commit IDs");
}
Defensive patterns

Strategy: validation

Validate before calling

// check gh output before handing to the review tool
let out = std::process::Command::new("gh").args(["pr","view",&num,"--json","baseRefOid,headRefOid"]).output()?;
let v: serde_json::Value = serde_json::from_slice(&out.stdout)?;
let is_full_sha = |s: &str| s.len() == 40 && s.chars().all(|c| c.is_ascii_hexdigit());
assert!(is_full_sha(v["baseRefOid"].as_str().unwrap_or("")) && is_full_sha(v["headRefOid"].as_str().unwrap_or("")), "gh did not return full commit SHAs");

Type guard

fn commit_id(s: &str) -> bool { s.len() == 40 && s.chars().all(|c| c.is_ascii_hexdigit()) }

Try / catch

match view_with(number, repo) {
    Err(e) if e.to_string().contains("exact base and head commit IDs") => eprintln!("gh returned incomplete metadata; upgrade gh and retry"),
    other => other?,
}

Prevention

When it happens

Trigger: `gh pr view` returns JSON whose baseRefOid or headRefOid is empty, abbreviated, or not a full 40-hex commit; the deserialize succeeds but the commit_id() check fails.

Common situations: Older or nonstandard gh CLI versions that omit/shorten oid fields; GitHub Enterprise with proxies that rewrite responses; a PR whose base branch was deleted so the base oid resolves oddly; corporate wrappers around gh emitting different JSON shapes.

Related errors


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

Appendix: source

Thrown at crates/tui/src/tools/review_pr.rs:90

        return false;
    };
    commit_id(old) && commit_id(new) && old.len() == new.len()
}

fn view_with(
    number: u32,
    repo: Option<&str>,
    run: &mut impl FnMut(Program, &[String]) -> Result<String>,
) -> Result<GhPullRequest> {
    if number == 0 {
        bail!("A positive pull request number is required");
    }
    let mut args = pr_args("view", number, repo);
    args.extend(["--json".into(), VIEW_FIELDS.into()]);
    let view: GhPullRequest = serde_json::from_str(&run(Program::Gh, &args)?)
        .context("gh pr view returned incomplete PR metadata")?;
    if !commit_id(&view.base_sha) || !commit_id(&view.head_sha) {
        bail!("gh pr view did not return exact base and head commit IDs");
    }
    Ok(view)
}

pub(crate) fn fetch_view(
    number: u32,
    repo: Option<&str>,
    workspace: &Path,
) -> Result<GhPullRequest> {
    view_with(number, repo, &mut |program, args| {
        run_command(workspace, program, args)
    })
}

fn same_revision(expected: &GhPullRequest, current: &GhPullRequest) -> Result<()> {
    if expected.head_sha != current.head_sha
        || expected.base_sha != current.base_sha
        || expected.changed_files != current.changed_files

View on GitHub (pinned to 73e0f67d83)