jdx/mise · error · eyre::Report
git failed with status {status}
Error message
git failed with status {status} What it means
run_command executes the side-effecting git operations (clone, `fetch --prune --tags`, checkout, `pull --ff-only`) without capturing output; a non-zero exit status bails as `git failed with status <code>` (e.g. exit status: 128). Because the child inherits the terminal, git's own error message appears directly above this bail in the output — read one line up for the real cause. A failure to spawn git at all produces the separate `git failed: <err>` message instead.
Source
Thrown at src/system/repos.rs:768
fn git_run(path: &Path, args: &[&str]) -> Result<()> {
let safe = format!("safe.directory={}", path.display());
run_command(
Command::new("git")
.arg("-C")
.arg(path)
.arg("-c")
.arg(safe)
.arg("-c")
.arg("core.autocrlf=false")
.args(args),
)
}
fn run_command(cmd: &mut Command) -> Result<()> {
debug!("$ {:?}", cmd);
let status = cmd.status().map_err(|err| eyre!("git failed: {err:#}"))?;
if !status.success() {
bail!("git failed with status {status}");
}
Ok(())
}
fn print_git_command(path: &Path, args: &[&str]) -> Result<()> {
let mut parts = vec![
"git".to_string(),
"-C".to_string(),
path.display().to_string(),
"-c".to_string(),
format!("safe.directory={}", path.display()),
"-c".to_string(),
"core.autocrlf=false".to_string(),
];
parts.extend(args.iter().map(|arg| arg.to_string()));
miseprintln!("{}", shell_words::join(parts));
Ok(())
}View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Look at git's own stderr line directly above the bail — it names the concrete cause
- For ff-only divergence: `git -C <repo> pull --rebase` or reset the branch to origin, then re-run
- For auth/network: confirm `git ls-remote <url>` works in the same environment (SSH agent, credentials)
- For stale pinned refs, update `ref` in mise.toml to a branch or tag that still exists
Example fix
# before: local commits make ff-only pull fail (status 128) $ git -C ~/src/x log --oneline origin/main..main # shows local commits # after $ git -C ~/src/x rebase origin/main $ mise bootstrap
Defensive patterns
Strategy: try-catch
Validate before calling
fn ff_pull_will_succeed(path: &Path, branch: &str) -> bool {
// local branch must not have commits missing from the remote tracking branch
std::process::Command::new("git")
.arg("-C").arg(path)
.args(["merge-base", "--is-ancestor", "HEAD", &format!("origin/{branch}")])
.status()
.map(|s| s.success())
.unwrap_or(false)
} Try / catch
match repos::update_statuses(&statuses, dry_run) {
Ok(()) => {}
Err(report) => {
let msg = format!("{report:#}");
if let Some(code) = msg.strip_prefix("git failed with status ") {
// git's own stderr was streamed to the terminal above this error;
// exit status 128 usually means ref/network/clone failure —
// rerun the exact printed command to reproduce
}
}
} Prevention
- Use `--dry-run` first: it prints the exact git commands (with -c safe.directory=...) to reproduce manually
- Never commit directly to branches bootstrap manages — let them stay pure fast-forward tracking branches
- Keep pinned `ref` values pointing at branches/tags that exist upstream
When it happens
Trigger: `git clone` denied by auth or unreachable host; `git pull --ff-only` rejecting because the local branch diverged from origin; cloning into an existing non-empty directory; checkout of a ref that no longer exists on the remote.
Common situations: Local commits on a branch bootstrap tries to fast-forward; a pinned branch or tag deleted upstream but still referenced in mise.toml; SSH keys or credential helpers unavailable in cron/CI where bootstrap runs.
Related errors
- git -C {} {} failed: {}
- repos: {} has local changes; commit, stash, or clean them be
- repos: {}: {reason}
- repos: command failed in {}
- no configured repo matched path: {filter}
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/f1ab776259da82e0.
Report an issue: GitHub.