kovidgoyal/kitty · error

Failed to resolve symlinks in %#v with error: %w

Error message

Failed to resolve symlinks in %#v with error: %w

What it means

Raised when filepath.EvalSymlinks fails on a copy source while the symlink strategy is not 'preserve'. The kitten tries to canonicalize the path (resolve symlink chains) before archiving, and the underlying OS call returned an error such as ENOENT or ELOOP.

Source

Thrown at kittens/ssh/config.go:248

		if err != nil {
			return nil, err
		}
		locations = append(locations, locs...)
	}
	if len(locations) == 0 {
		return nil, fmt.Errorf("No files to copy specified")
	}
	if len(locations) > 1 && opts.Dest != "" {
		return nil, fmt.Errorf("Specifying a remote location with more than one file is not supported")
	}
	home := paths_ctx.HomePath()
	ans = make([]*CopyInstruction, 0, len(locations))
	for _, loc := range locations {
		ci := CopyInstruction{local_path: loc, exclude_patterns: opts.Exclude}
		if opts.SymlinkStrategy != "preserve" {
			ci.local_path, err = filepath.EvalSymlinks(loc)
			if err != nil {
				return nil, fmt.Errorf("Failed to resolve symlinks in %#v with error: %w", loc, err)
			}
		}
		if opts.SymlinkStrategy == "resolve" {
			ci.arcname = get_arcname(ci.local_path, opts.Dest, home)
		} else {
			ci.arcname = get_arcname(loc, opts.Dest, home)
		}
		ans = append(ans, &ci)
	}
	return
}

type file_unique_id struct {
	dev, inode uint64
}

func excluded(pattern, path string) bool {
	if !strings.ContainsRune(pattern, '/') {

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Inspect the path: readlink -f <path> to see where it fails
  2. Remove or fix the dangling/looping symlink
  3. Set symlink strategy to 'preserve' if you want such symlinks passed through unchanged

Example fix

# before (default resolves symlinks)
kitty +kitten ssh --copy "~/.some/link" user@host
# after
kitty +kitten ssh --symlink-strategy preserve --copy "~/.some/link" user@host
Defensive patterns

Strategy: validation

Validate before calling

if _, err := filepath.EvalSymlinks(path); err != nil {
    // broken link: skip or use symlink_strategy=preserve
}

Try / catch

if _, err := os.Readlink(path); err == nil {
    if _, terr := filepath.EvalSymlinks(path); terr != nil {
        // pass through with preserve strategy instead
    }
}

Prevention

When it happens

Trigger: ParseCopyInstruction with symlink strategy 'resolve' (default) on a path that is a dangling symlink, a symlink loop, or a path whose target was removed between globbing and resolution.

Common situations: Copying from directories containing broken editor backup links or /tmp symlinks to deleted files; race with a cleanup job deleting the target; symlink loops in dotfiles repos.

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/0799ac8969a4d9dd. Report an issue: GitHub.