{"record":{"id":"690967cd118aa29c","repo":"kovidgoyal/kitty","slug":"resolving-base-symlinks-w","errorCode":null,"errorMessage":"resolving base symlinks: %w","messagePattern":"resolving base symlinks: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tools/utils/paths.go","lineNumber":349,"sourceCode":"\treturn\n}\n\n// RelativeIfUnder returns the path to 'target' relative to 'base' if and only if\n// target is inside base. It returns the relative path, a boolean indicating\n// whether target is inside base, and an error.\n//\n// If resolveSymlinks is true, both base and target are run through filepath.EvalSymlinks\n// before containment checks. If base == target the function returns \".\" and true.\n//\n// Notes:\n//   - This uses filepath.Rel and then checks for leading \"..\" components to determine\n//     whether the returned relative path escapes the base directory.\n//   - On Windows behaviour is consistent with filepath semantics.\nfunc RelativeIfUnder(base, target string, resolveSymlinks bool) (rel string, inside bool, err error) {\n\t// Optionally resolve symlinks first\n\tif resolveSymlinks {\n\t\tif base, err = filepath.EvalSymlinks(base); err != nil {\n\t\t\treturn \"\", false, fmt.Errorf(\"resolving base symlinks: %w\", err)\n\t\t}\n\t\tif target, err = filepath.EvalSymlinks(target); err != nil {\n\t\t\treturn \"\", false, fmt.Errorf(\"resolving target symlinks: %w\", err)\n\t\t}\n\t}\n\n\t// Make absolute and clean\n\tif base, err = filepath.Abs(base); err != nil {\n\t\treturn \"\", false, fmt.Errorf(\"abs base: %w\", err)\n\t}\n\tif target, err = filepath.Abs(target); err != nil {\n\t\treturn \"\", false, fmt.Errorf(\"abs target: %w\", err)\n\t}\n\n\t// On Windows the volume (drive letter) must match. If they don't, the path is not inside.\n\tif runtime.GOOS == \"windows\" {\n\t\tif !strings.EqualFold(filepath.VolumeName(base), filepath.VolumeName(target)) {\n\t\t\treturn \"\", false, nil","sourceCodeStart":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/kovidgoyal/kitty/blob/6d5d0c440603ad9bdf6dcd599f73f6dde21acb44/tools/utils/paths.go#L331-L367","documentation":"RelativeIfUnder computes whether target lies under base, optionally resolving symlinks first with filepath.EvalSymlinks. This variant fires when EvalSymlinks fails on the base path: the base path does not exist, contains a symlink loop, or is inaccessible.","triggerScenarios":"Calling RelativeIfUnder(base, target, true) where base doesn't exist (e.g. a config dir not yet created), a symlink cycle in the base chain, or permission denied on a path component. On macOS, /tmp vs /private/tmp mismatches also force symlink resolution paths.","commonSituations":"Applying shell-relative path logic to a not-yet-created output directory, or a base path from user config pointing at a broken symlink.","solutions":["Ensure the base directory exists (os.MkdirAll) before calling with resolveSymlinks=true.","Fix or remove broken/cyclic symlinks in the base path (readlink -f to diagnose).","Pass resolveSymlinks=false if symlink semantics are not required.","Check permissions on each component of the base path."],"exampleFix":"// before\nrel, inside, err := utils.RelativeIfUnder(outDir, f, true)\n// after\n_ = os.MkdirAll(outDir, 0o755)\nrel, inside, err := utils.RelativeIfUnder(outDir, f, true)","handlingStrategy":"validation","validationCode":"if _, err := filepath.EvalSymlinks(base); err != nil {\n    _ = os.MkdirAll(base, 0o755) // create before resolving\n}","typeGuard":"func baseResolvable(base string) bool {\n    _, err := filepath.EvalSymlinks(base)\n    return err == nil\n}","tryCatchPattern":"rel, inside, err := utils.RelativeIfUnder(base, target, true)\nif err != nil && strings.HasPrefix(err.Error(), \"resolving base\") {\n    rel, inside, err = utils.RelativeIfUnder(base, target, false)\n}","preventionTips":["MkdirAll the base directory before symlink-resolving calls.","readlink -f base to detect broken/cyclic symlinks early.","Keep base paths absolute and existing."],"tags":["go","filesystem","symlink","paths"],"backgroundTag":"path-resolution-failed","analyzedSha":"6d5d0c440603ad9bdf6dcd599f73f6dde21acb44","analyzedAt":"2026-08-27T14:20:20.142Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}