kovidgoyal/kitty · warning

computing relative path: %w

Error message

computing relative path: %w

What it means

RelativeIfUnder finishes with filepath.Rel(base, target), which computes the relative path. Rel can only fail when it cannot make target relative to base — on Windows when the paths are on different volumes (e.g. C:\ vs D:\); on Unix this is practically unreachable because Abs+Clean normalizes both.

Source

Thrown at tools/utils/paths.go:374

	// Make absolute and clean
	if base, err = filepath.Abs(base); err != nil {
		return "", false, fmt.Errorf("abs base: %w", err)
	}
	if target, err = filepath.Abs(target); err != nil {
		return "", false, fmt.Errorf("abs target: %w", err)
	}

	// On Windows the volume (drive letter) must match. If they don't, the path is not inside.
	if runtime.GOOS == "windows" {
		if !strings.EqualFold(filepath.VolumeName(base), filepath.VolumeName(target)) {
			return "", false, nil
		}
	}

	// Get the relative path from base to target
	rel, err = filepath.Rel(base, target)
	if err != nil {
		return "", false, fmt.Errorf("computing relative path: %w", err)
	}

	// If rel begins with ".." (or is ".."), then target is outside base.
	// Use os.PathSeparator to be portable.
	up := ".." + string(os.PathSeparator)
	if rel == ".." || strings.HasPrefix(rel, up) {
		return "", false, nil
	}

	// If the returned rel is empty (shouldn't normally happen), normalize to "."
	if rel == "" {
		rel = "."
	}
	return rel, true, nil
}

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Guard on Windows by comparing filepath.VolumeName(base) and VolumeName(target) first.
  2. Map both paths to a common volume (subst/junction) before comparison.
  3. Treat this error as "not inside" and skip the relative-path optimization for that target.
  4. Report both paths in logs to identify the volume mismatch.

Example fix

// before
rel, inside, err := utils.RelativeIfUnder(base, target, false)
// after
rel, inside, err := utils.RelativeIfUnder(base, target, false)
if err != nil { // e.g. cross-volume on Windows
    rel, inside, err = target, false, nil
}
Defensive patterns

Strategy: fallback

Validate before calling

if runtime.GOOS == "windows" && !strings.EqualFold(filepath.VolumeName(base), filepath.VolumeName(target)) {
    // skip relative-path handling; different volumes
}

Try / catch

rel, inside, err := utils.RelativeIfUnder(base, target, false)
if err != nil {
    rel, inside = "", false // treat as outside base; use absolute target
}

Prevention

When it happens

Trigger: Windows-only: base and target on different drive letters after volume-name check passes (e.g. UNC vs lettered drive mixtures), causing filepath.Rel to return an error.

Common situations: Cross-drive comparisons on Windows, e.g. base under C:\Users and target as a UNC \\server\share path.

Related errors


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