{"record":{"id":"711503c9f3ceb3c5","repo":"dagger/dagger","slug":"readfile-s","errorCode":null,"errorMessage":"readFile %s","messagePattern":"readFile (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/fsutil/followlinks.go","lineNumber":163,"sourceCode":"\terr := fs.Walk(context.TODO(), root, func(p string, entry os.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif p != root {\n\t\t\treturn errors.Errorf(\"expected single entry %q but got %q\", root, p)\n\t\t}\n\t\tout = entry\n\t\tif entry.IsDir() {\n\t\t\treturn filepath.SkipDir\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif out == nil {\n\t\treturn nil, errors.Wrapf(os.ErrNotExist, \"readFile %s\", root)\n\t}\n\treturn out, nil\n}\n\nfunc readDir(fs FS, root string) ([]os.DirEntry, error) {\n\tvar out []os.DirEntry\n\n\troot = filepath.FromSlash(filepath.Clean(root))\n\tif root == string(filepath.Separator) || root == \".\" {\n\t\troot = \".\"\n\t\tout = make([]gofs.DirEntry, 0)\n\t}\n\n\terr := fs.Walk(context.TODO(), root, func(p string, entry os.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif p == root {","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/internal/fsutil/followlinks.go#L145-L181","documentation":"statFile returns os.ErrNotExist wrapped as \"readFile %s\" when its walk over the FS completed without producing any entry for the root. This is the not-found signal that readSymlink translates into 'no symlink here' (returns nil targets), so callers usually see the path treated as absent.","triggerScenarios":"FollowLinks/NewFilterFS resolving a FollowPaths entry (or wildcard match) where statFile's walk finds nothing — the path doesn't exist in the underlying FS snapshot, or the root is \"/\" or \".\" which short-circuits to nil entry and then ErrNotExist.","commonSituations":"FollowPaths listing a symlink that was deleted or never existed in the snapshot, a typo in the followed path, or a wildcard that matched names (via readDir) pointing at paths absent from the FS.","solutions":["Confirm each FollowPaths entry exists in the FS at NewFilterFS time; remove stale entries","Avoid following the FS root itself; FollowLinks paths like \"/\" or \".\" short-circuit and resolve to nothing meaningful","Handle not-found gracefully in callers — readSymlink already swallows isNotFound, so surface only if it escapes via a different wrap"],"exampleFix":"// before\nFollowPaths: []string{\"bin/dagger\"} // missing in snapshot\n// after\n// ensure bin/dagger exists in the FS before NewFilterFS, or drop it from FollowPaths","handlingStrategy":"validation","validationCode":"// Confirm each FollowPaths entry exists before NewFilterFS\nfor _, p := range opt.FollowPaths {\n\tif _, err := underlying.Open(filepath.Clean(p)); err != nil {\n\t\treturn fmt.Errorf(\"FollowPaths entry missing: %s\", p)\n\t}\n}","typeGuard":null,"tryCatchPattern":"targets, err := fsutil.FollowLinks(fs, paths)\nif err != nil {\n\tif errors.Is(err, os.ErrNotExist) { return nil, nil } // absent link: no targets\n\treturn err\n}","preventionTips":["Build FollowPaths from paths verified to exist in the snapshot","Watch for '/' or '.' entries which short-circuit to no entry","Re-check symlink existence after snapshot/copy operations"],"tags":["fs","symlink","not-found"],"backgroundTag":"file-not-found","analyzedSha":"82ba2681dbe30d3547a1dc50ea495900ab5b6047","analyzedAt":"2026-09-05T07:21:37.930Z","contentChangedAt":"2026-09-05T07:21:37.930Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}