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

  1. Look at git's own stderr line directly above the bail — it names the concrete cause
  2. For ff-only divergence: `git -C <repo> pull --rebase` or reset the branch to origin, then re-run
  3. For auth/network: confirm `git ls-remote <url>` works in the same environment (SSH agent, credentials)
  4. 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

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


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/f1ab776259da82e0. Report an issue: GitHub.