GitoxideLabs/gitoxide · error · anyhow::Error
external diff exited with
Error message
external diff exited with {status} What it means
The configured external diff program (diff.external / GIT_EXTERNAL_DIFF) exited with a status other than 0 or 1. Exit 1 is tolerated because diff tools conventionally use it to mean 'files differ'; anything else indicates the tool itself failed.
Solutions
- Run the external diff command manually with the same arguments to see its real error.
- Fix or replace diff.external in config (or unset GIT_EXTERNAL_DIFF) and re-run.
- Make your custom diff tool return 0 (same) or 1 (differ) and reserve other codes for real failures.
- If the tool crashes on this input, test it on the specific files involved.
Example fix
// before #!/bin/sh compare "$@" # exit 2 on usage errors // after #!/bin/sh compare "$@" || exit 1 # map failures to diff-style exit codes
Defensive patterns
Strategy: validation
Validate before calling
let cmd = repo.config_snapshot().trusted_program(gix::config::tree::Diff::EXTERNAL)?; // test it: run cmd with two temp files and require exit 0 or 1
Try / catch
match show_external_diff(...) {
Err(err) if err.to_string().starts_with("external diff exited with") => {
eprintln!("{}; falling back to built-in diff", err);
}
other => other?,
} Prevention
- Test the external diff tool on sample files before configuring it.
- Ensure the tool only exits 0 or 1 for normal operation.
- Keep diff.external simple; avoid scripts with unhandled usage errors.
When it happens
Trigger: Running a file diff through the external diff command, which exits with e.g. 2 (usage error), 127 (command shell issue), 127/126, or a crash exit code.
Common situations: Misconfigured diff.external pointing to a broken script; external tool missing dependencies; tool crashing on binary/large files; wrong argument conventions.
Related errors
- diff pager exited with
- Source or destination is binary and we can't diff that
- Editor exited with
- {}
- Git editor exited with
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/6ec2969e7ee6e4bd.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/lib.rs:6099
for reference in &group.references {
let mut refspec = b":".to_vec();
refspec.extend_from_slice(reference.as_bstr());
command.arg(gix::path::from_bstr(refspec.as_bstr()).as_ref());
}
match command.status() {
Ok(status) if status.success() => outcome.deleted += group.references.len(),
Ok(status) => outcome.failures.push(format!("{} exited with {status}", group.remote)),
Err(err) => outcome.failures.push(format!("{}: {err}", group.remote)),
}
}
outcome
}
fn external_diff_status(status: ExitStatus) -> Result<()> {
if status.success() || status.code() == Some(1) {
Ok(())
} else {
anyhow::bail!("external diff exited with {status}")
}
}
fn pager_write_result(result: io::Result<()>) -> Result<()> {
match result {
Err(err) if err.kind() == io::ErrorKind::BrokenPipe => Ok(()),
result => result.context("could not write diff to pager"),
}
}
fn pager_status(status: ExitStatus) -> Result<()> {
if status.success() {
Ok(())
} else {
anyhow::bail!("diff pager exited with {status}")
}
}
View on GitHub (pinned to e73179060b)