xai-org/grok-build · error

git checkout {} failed: {}

Error message

git checkout {} failed: {}

What it means

`checkout_ref` runs `git checkout <ref>` in the worktree and bails with this message (ref plus git's stderr) when the command exits non-zero. Git refused the checkout, so the worktree remains on its previous ref/state.

Source

Thrown at crates/codegen/xai-fast-worktree/src/git/checkout.rs:88

            "git clean {} failed: {}",
            flags,
            String::from_utf8_lossy(&output.stderr)
        );
    }

    Ok(())
}

/// Run `git checkout <ref>`. Blocking.
pub(crate) fn checkout_ref(worktree_path: &Path, git_ref: &str) -> Result<()> {
    let output = git_command()
        .current_dir(worktree_path)
        .args(["checkout", git_ref])
        .output()
        .context("failed to run git checkout")?;

    if !output.status.success() {
        anyhow::bail!(
            "git checkout {} failed: {}",
            git_ref,
            String::from_utf8_lossy(&output.stderr)
        );
    }

    tracing::debug!(path = %worktree_path.display(), git_ref = %git_ref, "git checkout");
    Ok(())
}

/// Whether `git diff-index --quiet HEAD` reports differences (tracked-only, far
/// cheaper than `git status`). `cached` adds `--cached`, comparing the index to
/// `HEAD` and skipping the working-tree stat walk. Only ever over-reports (an
/// error is treated as dirty), so a `false` result is safe to skip the reset on.
/// Blocking.
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
fn diff_index_dirty(worktree_path: &Path, cached: bool) -> Result<bool> {
    let mut cmd = git_command();

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Read git's stderr in the message — it distinguishes 'unknown revision', 'would be overwritten', and lock errors.
  2. Verify the ref exists (`git rev-parse --verify <ref>`) and fetch it if missing before retrying.
  3. Clear conflicting state: commit/stash or remove untracked files that block the checkout (`git clean`/`git stash -u`), or checkout with a reset flow.
  4. Remove a stale `index.lock` if no git process is running, then retry.
  5. Fetch missing objects for shallow/partial clones (`git fetch origin <ref>`) and retry.

Example fix

// before
// untracked build.log would be overwritten by target branch
checkout_ref(worktree, "main")?; // git checkout main failed: untracked working tree files would be overwritten
// after
run_in(worktree, &["git", "clean", "-fd"]); // or stash untracked files
checkout_ref(worktree, "main")?;
Defensive patterns

Strategy: try-catch

Validate before calling

use std::process::Command;
use std::path::Path;
fn can_checkout(worktree: &Path, git_ref: &str) -> bool {
    let ref_ok = Command::new("git").current_dir(worktree)
        .args(["rev-parse", "--verify", "--quiet", git_ref])
        .output().map(|o| o.status.success()).unwrap_or(false);
    let no_lock = !worktree.join(".git").join("index.lock").exists();
    ref_ok && no_lock
}

Try / catch

match checkout_ref(worktree, git_ref) {
    Err(e) if e.to_string().contains("git checkout") => {
        eprintln!("checkout failed: {e}"); // stderr embedded
        // clean/stash blockers or fetch the ref, then retry once
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling `checkout_ref` with a ref that doesn't exist, when untracked files would be overwritten by checkout, when the index is locked, or when the worktree's git metadata is invalid.

Common situations: Typo'd or deleted branch/tag names; local untracked/modified files conflicting with the target ref; shallow or partial clones lacking target objects; concurrent git operations leaving `index.lock`; detached-HEAD worktree restrictions.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/4a725a4494d03ae4. Report an issue: GitHub.