kovidgoyal/kitty · error
Failed to diff %s vs. %s with errors: %s
Error message
Failed to diff %s vs. %s with errors: %s
What it means
do_diff shells out to the configured diff command via run_diff; when the command reports failure (non-zero exit other than the expected 'differences found' code, or the wrapper signals an error), the kitten surfaces this error embedding both paths and the raw output from the command. It means the external diff process itself failed, not that differences exist.
Source
Thrown at kittens/diff/patch.go:596
c := exec.Command(cmd[0], cmd[1:]...)
stdout, stderr := bytes.Buffer{}, bytes.Buffer{}
c.Stdout, c.Stderr = &stdout, &stderr
err = c.Run()
if err != nil {
var e *exec.ExitError
if errors.As(err, &e) && e.ExitCode() == 1 {
return true, true, stdout.String(), nil
}
return false, false, stderr.String(), err
}
return true, false, stdout.String(), nil
}
}
func do_diff(file1, file2 string, context_count int) (ans *Patch, err error) {
ok, _, raw, err := run_diff(file1, file2, context_count)
if !ok {
return nil, fmt.Errorf("Failed to diff %s vs. %s with errors:\n%s", file1, file2, raw)
}
if err != nil {
return
}
left_lines, err := lines_for_path(file1)
if err != nil {
return
}
right_lines, err := lines_for_path(file2)
if err != nil {
return
}
ans, err = parse_patch(raw, left_lines, right_lines)
return
}
type diff_job struct{ file1, file2 string }
View on GitHub (pinned to 6d5d0c4406)
Solutions
- Read the embedded raw output in the message: it is the external tool's own error text and names the real cause.
- Verify diff_cmd exists and is executable in kitty's environment (`which` inside kitty shell).
- Make your custom diff wrapper exit 0 for 'same', 1 for 'differences', and >=2 only for genuine errors, GNU diff-style.
- Handle files the tool can't process (skip binaries) or quote paths properly in the wrapper.
Example fix
# before (custom diff_cmd wrapper) #!/bin/sh exec my-differ "$1" "$2" # exits 3 on any diff # after #!/bin/sh exec my-differ "$1" "$2" rc=$? [ $rc -eq 3 ] && exit 1 exit $rc
Defensive patterns
Strategy: fallback
Validate before calling
// Go: pre-flight the configured diff command
if _, err := exec.LookPath(diffCmd); err != nil {
return fmt.Errorf("diff_cmd %q not found in PATH", diffCmd)
} Try / catch
Catch errors from do_diff; parse the embedded tool output to classify the failure. For transient issues (missing file mid-compare) retry once; for exit-code conventions, fall back to GNU diff; otherwise surface the tool's stderr verbatim.
Prevention
- Make custom wrappers exit 0/1 like GNU diff, reserving >=2 for real errors.
- Use LookPath pre-flight checks for the configured diff_cmd.
- Quote all file arguments in wrapper scripts; keep environments (PATH) consistent.
When it happens
Trigger: `diff_cmd` in kitty.conf pointing to a missing or non-executable binary; the diff command erroring on its inputs (binary files with a tool that refuses them, permission denied, missing file, out of memory); a wrapper script exiting non-zero and printing diagnostics to stderr which get captured into the message.
Common situations: Custom diff_cmd wrappers returning the wrong exit codes (the kitten expects 0/1 a la GNU diff; 2+ means error); diffing files the configured tool can't handle (binary, huge, special paths with spaces if unquoted); PATH differences between interactive shell and kitty's environment.
Related errors
- You must specify exactly two files/directories to compare
- The items to be diffed should both be either directories or
- This terminal does not support the kitty keyboard protocol,
- Left side line mismatch %d != %d
- Right side line mismatch %d != %d
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/74f06e4b378778b4.
Report an issue: GitHub.