jdx/mise · error

git diff failed: {}

Error message

git diff failed: {}

What it means

In `decrypted_diff`, mise shells out to `git diff --no-index --color=never` to compare two decrypted dotfile snapshots. Git's exit codes 0 (no differences) and 1 (differences found) are both accepted; any other exit code means git itself failed (bad arguments, git not functioning, unreadable temp files). The error wraps git's stderr so the developer can see why the diff invocation broke.

Source

Thrown at src/system/history/shadow.rs:1001

                && mode != "160000"
            {
                right_file.write_all(&self.cat_object(oid)?)?;
            }
            let left_name = left_file.path().to_string_lossy().to_string();
            let right_name = right_file.path().to_string_lossy().to_string();
            let diff = self.git.output_unchecked(PlumbingCall::new([
                "diff",
                "--no-index",
                "--no-ext-diff",
                "--no-textconv",
                "--patch",
                "--color=never",
                "--",
                &left_name,
                &right_name,
            ]))?;
            if !matches!(diff.status.code(), Some(0 | 1)) {
                bail!("git diff failed: {}", String::from_utf8_lossy(&diff.stderr));
            }
            let text = String::from_utf8_lossy(&diff.stdout)
                .replace(left_name.trim_start_matches('/'), &left_path)
                .replace(right_name.trim_start_matches('/'), &right_path);
            output.extend_from_slice(text.as_bytes());
            if left.as_ref().map(|v| &v.0) != right.as_ref().map(|v| &v.0) {
                output.extend_from_slice(
                    format!(
                        "{right_path}: mode {} -> {}\n",
                        left.as_ref().map_or("absent", |v| v.0.as_str()),
                        right.as_ref().map_or("absent", |v| v.0.as_str())
                    )
                    .as_bytes(),
                );
            }
        }
        Ok(DiffResult { output, changed })
    }

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Run `git diff --no-index --color=never <left> <right>` manually on the two paths from the error context and read the stderr shown in the message.
  2. Verify `git --version` works and no global git config (e.g. a broken external diff driver) interferes.
  3. Check that the temporary directory holding the decrypted snapshots exists and is readable/writable by the current user.
  4. Retry the dotfile diff command; if it persists, report with the captured stderr.

Example fix

// before: opaque failure
let diff = cmd_output(...)?;
if !matches!(diff.status.code(), Some(0 | 1)) {
    bail!("git diff failed: {}", String::from_utf8_lossy(&diff.stderr));
}
// after (caller-side): pre-check inputs and git availability
if !left_path.exists() || !right_path.exists() {
    eprintln!("snapshot missing; re-running sync");
}
assert!(Command::new("git").arg("--version").output().is_ok());
Defensive patterns

Strategy: try-catch

Validate before calling

if !Command::new("git").arg("--version").output().map(|o| o.status.success()).unwrap_or(false) {
    eprintln!("git unavailable; diff will fail");
}
assert!(left_path.exists() && right_path.exists());

Type guard

fn diffable(left: &Path, right: &Path) -> bool { left.exists() && right.exists() }

Try / catch

match store.diff(&left, &right) {
    Ok(text) => display(text),
    Err(e) if e.to_string().contains("git diff failed") => eprintln!("git diff broke: check git install/temp dir: {e}"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `diff` on two shadow-history snapshots when the spawned `git diff` process exits with a code other than 0 or 1 — e.g. git is missing/broken, the temp file paths are invalid or deleted mid-diff, or an invalid diff option is in effect.

Common situations: A corrupt or pruned temp directory removes one of the compared files before git reads it; a system git is too old or misconfigured (broken global diff driver); a sandboxed environment blocks process spawning or /tmp access.

Understand the failure class

Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/da136197bd615ad4. Report an issue: GitHub.