{"record":{"id":"74f06e4b378778b4","repo":"kovidgoyal/kitty","slug":"failed-to-diff-s-vs-s-with-errors-s","errorCode":null,"errorMessage":"Failed to diff %s vs. %s with errors:\n%s","messagePattern":"Failed to diff (.+?) vs\\. (.+?) with errors:\n(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kittens/diff/patch.go","lineNumber":596,"sourceCode":"\t\tc := exec.Command(cmd[0], cmd[1:]...)\n\t\tstdout, stderr := bytes.Buffer{}, bytes.Buffer{}\n\t\tc.Stdout, c.Stderr = &stdout, &stderr\n\t\terr = c.Run()\n\t\tif err != nil {\n\t\t\tvar e *exec.ExitError\n\t\t\tif errors.As(err, &e) && e.ExitCode() == 1 {\n\t\t\t\treturn true, true, stdout.String(), nil\n\t\t\t}\n\t\t\treturn false, false, stderr.String(), err\n\t\t}\n\t\treturn true, false, stdout.String(), nil\n\t}\n}\n\nfunc do_diff(file1, file2 string, context_count int) (ans *Patch, err error) {\n\tok, _, raw, err := run_diff(file1, file2, context_count)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"Failed to diff %s vs. %s with errors:\\n%s\", file1, file2, raw)\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\tleft_lines, err := lines_for_path(file1)\n\tif err != nil {\n\t\treturn\n\t}\n\tright_lines, err := lines_for_path(file2)\n\tif err != nil {\n\t\treturn\n\t}\n\tans, err = parse_patch(raw, left_lines, right_lines)\n\treturn\n}\n\ntype diff_job struct{ file1, file2 string }\n","sourceCodeStart":578,"sourceCodeEnd":614,"githubUrl":"https://github.com/kovidgoyal/kitty/blob/6d5d0c440603ad9bdf6dcd599f73f6dde21acb44/kittens/diff/patch.go#L578-L614","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","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."],"exampleFix":"# before (custom diff_cmd wrapper)\n#!/bin/sh\nexec my-differ \"$1\" \"$2\"  # exits 3 on any diff\n\n# after\n#!/bin/sh\nexec my-differ \"$1\" \"$2\"\nrc=$?\n[ $rc -eq 3 ] && exit 1\nexit $rc","handlingStrategy":"fallback","validationCode":"// Go: pre-flight the configured diff command\nif _, err := exec.LookPath(diffCmd); err != nil {\n    return fmt.Errorf(\"diff_cmd %q not found in PATH\", diffCmd)\n}","typeGuard":null,"tryCatchPattern":"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.","preventionTips":["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."],"tags":["kitty","diff-kitten","external-tool","diff-cmd","process-exit-code"],"backgroundTag":"external-command-failed","analyzedSha":"6d5d0c440603ad9bdf6dcd599f73f6dde21acb44","analyzedAt":"2026-08-27T14:20:20.142Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}