{"record":{"id":"a66df70663e6c25e","repo":"air-verse/air","slug":"unexpected-error-while-dereferencing-v-w","errorCode":null,"errorMessage":"unexpected error while dereferencing %v: %w","messagePattern":"unexpected error while dereferencing (.+?): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"runner/util.go","lineNumber":290,"sourceCode":"\n\tif strings.HasPrefix(path, \"~/\") {\n\t\thome := os.Getenv(\"HOME\")\n\t\texpanded = filepath.Join(home, path[1:])\n\t}\n\n\texpanded, err := filepath.Abs(expanded)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"error getting absolute path to %v: %w\", expanded, err)\n\t}\n\n\t// filepath.EvalSymlinks only works on real files\n\tdereferenced, err := filepath.EvalSymlinks(expanded)\n\tif err == nil {\n\t\treturn dereferenced, nil\n\t}\n\n\tif !errors.Is(err, fs.ErrNotExist) {\n\t\treturn \"\", fmt.Errorf(\"unexpected error while dereferencing %v: %w\", expanded, err)\n\t}\n\n\treturn expanded, nil\n}\n\nfunc isDir(path string) bool {\n\ti, err := os.Stat(path)\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn i.IsDir()\n}\n\nfunc validEvent(ev fsnotify.Event) bool {\n\treturn ev.Op&fsnotify.Create == fsnotify.Create ||\n\t\tev.Op&fsnotify.Write == fsnotify.Write ||\n\t\tev.Op&fsnotify.Remove == fsnotify.Remove ||\n\t\tev.Op&fsnotify.Rename == fsnotify.Rename","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/air-verse/air/blob/71ea1dee05248122ed22b5870c7fb20a90d80ddd/runner/util.go#L272-L308","documentation":"After absolutizing, expandPath calls filepath.EvalSymlinks to dereference the path. EvalSymlinks errors that are NOT fs.ErrNotExist (e.g. ELOOP symlink cycles, EACCES) are unexpected and returned wrapped in this error.","triggerScenarios":"expandPath receives a path whose symlink resolution fails with a non-ErrNotExist error: a symlink loop, permission denied on a parent directory, or too many levels of symbolic links during config preprocessing.","commonSituations":"Circular symlinks in the project or home directory; a symlinked project path where a component is unreadable after a permissions change; NFS/mount issues making components inaccessible.","solutions":["Inspect the wrapped %w error (syscall.ELOOP → symlink cycle; EACCES → permissions) and fix accordingly","Remove or repair circular symlinks in the path (`find -L . -type l` can help locate loops)","Check permissions on every path component (`ls -la` each ancestor)","Bypass the symlink by configuring air with the real, non-symlinked path"],"exampleFix":"// before: project is a symlink loop\nln -s proj proj-link && ln -s proj-link proj\n// after: point config at the real directory\nroot = \"/home/user/proj\"  // actual directory, not a loop","handlingStrategy":"try-catch","validationCode":"// check the path resolves cleanly before handing it to air\nresolved, err := filepath.EvalSymlinks(projectDir)\nif err != nil {\n\treturn fmt.Errorf(\"project path not resolvable: %w\", err)\n}\n_ = resolved","typeGuard":null,"tryCatchPattern":"_, err := filepath.EvalSymlinks(dir)\nif err != nil {\n\tswitch {\n\tcase errors.Is(err, syscall.ELOOP):\n\t\t// fix symlink cycle\n\tcase errors.Is(err, fs.ErrPermission):\n\t\t// fix permissions\n\t}\n\treturn err\n}","preventionTips":["Avoid symlink loops in project or home directories","Point air at real directories rather than chains of symlinks","Keep parent directories of the project readable/executable","Audit paths with `ls -la` and `find -L -type l` after environment changes"],"tags":["filesystem","symlink","path"],"backgroundTag":"symlink-resolution-failure","analyzedSha":"71ea1dee05248122ed22b5870c7fb20a90d80ddd","analyzedAt":"2026-08-31T20:27:54.676Z","schemaVersion":2},"datasetVersion":"2026-09-01T03:17:15.561Z"}