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
- Read git's stderr in the message — it distinguishes 'unknown revision', 'would be overwritten', and lock errors.
- Verify the ref exists (`git rev-parse --verify <ref>`) and fetch it if missing before retrying.
- Clear conflicting state: commit/stash or remove untracked files that block the checkout (`git clean`/`git stash -u`), or checkout with a reset flow.
- Remove a stale `index.lock` if no git process is running, then retry.
- 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
- Verify the ref exists and is fetched before checking out
- Run git clean or stash before switching refs so untracked files don't block checkout
- Remove stale index.lock files after crashed git operations
- Prefer unique, fully qualified refs (refs/heads/main) to avoid ambiguity
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
- git reset --hard {} failed: {}
- git clean {} failed: {}
- git worktree add failed: {}
- git worktree add failed: {}
- working tree has uncommitted changes; commit or stash before
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/4a725a4494d03ae4.
Report an issue: GitHub.