kovidgoyal/kitty · error
The items to be diffed should both be either directories or
Error message
The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.'
What it means
The diff kitten resolves both positional paths and checks their filesystem types before starting the TUI. It refuses to compare a directory with a regular file because its diff driver (configurable via diff_cmd) operates on two like-typed inputs. The check uses the resolved (symlink-followed) type of each path.
Source
Thrown at kittens/diff/main.go:167
}
right, err := get_remote_file(args[1])
if err != nil {
return 1, err
}
defer func() {
for _, path := range temp_files {
os.Remove(path)
}
}()
var left_is_dir, right_is_dir bool
if left, left_is_dir, err = resolve_path(left); err != nil {
return 1, err
}
if right, right_is_dir, err = resolve_path(right); err != nil {
return 1, err
}
if left_is_dir != right_is_dir {
return 1, fmt.Errorf("The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.'")
}
lp, err = loop.New()
loop.MouseTrackingMode(lp, loop.BUTTONS_AND_DRAG_MOUSE_TRACKING)
if err != nil {
return 1, err
}
lp.ColorSchemeChangeNotifications()
h := Handler{left: left, right: right, lp: lp}
lp.OnInitialize = func() (string, error) {
lp.SetCursorVisible(false)
lp.SetCursorShape(loop.BAR_CURSOR, true)
lp.AllowLineWrapping(false)
lp.SetWindowTitle(fmt.Sprintf("%s vs. %s", left, right))
lp.QueryCapabilities()
h.initialize()
return "", nil
}View on GitHub (pinned to 6d5d0c4406)
Solutions
- Make both sides the same type: either two files or two directories.
- If comparing a file to a directory, pick the specific file inside the directory to compare against.
- If symlinks are involved, check what they resolve to (ls -l / readlink -f) and point at the real targets.
Example fix
# before kitty +kitten diff ./config.toml ./backup/ # after kitty +kitten diff ./config.toml ./backup/config.toml
Defensive patterns
Strategy: validation
Validate before calling
// Go: ensure both sides are the same kind before launching
st1, err1 := os.Stat(left)
st2, err2 := os.Stat(right)
if err1 != nil || err2 != nil {
log.Fatal("stat failed", err1, err2)
}
if st1.IsDir() != st2.IsDir() {
log.Fatal("both paths must be files or both directories")
} Type guard
func sameKind(a, b string) bool {
sa, ea := os.Stat(a)
sb, eb := os.Stat(b)
return ea == nil && eb == nil && sa.IsDir() == sb.IsDir()
} Try / catch
On error, inspect the message text; if it mentions 'directory to a file', resolve the intended file inside the directory and retry once with corrected paths.
Prevention
- Stat both paths and compare IsDir() before invoking.
- Resolve symlinks (filepath.EvalSymlinks) so types reflect real targets.
- In scripts, guard against accidentally passing a directory where a file is expected.
When it happens
Trigger: Calling `kitty +kitten diff file.txt somedir/` or `kitty +kitten diff dir/ file.txt`; a symlink on one side resolving to a different type than the other side appears to be; a path that is a socket/fifo/device being classified differently than the plain file on the other side.
Common situations: Scripts that compare `$file` against a backup directory; mistyping one side so it hits a directory; a symlinked config path pointing to a directory while the other side is a file; NFS or FUSE mounts reporting odd file types.
Related errors
- failed to read cached image metadata: %w
- failed to read cached image frame %d data: %w
- failed to read the directory %s with error: %w
- Failed to rename temporary file used for downloading to dest
- Could not find any writable data directories. Make sure XDG_
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/c6c9e57af3d7d7e4.
Report an issue: GitHub.