{"record":{"id":"229b65d91a606c38","repo":"dagger/dagger","slug":"readdir-s","errorCode":null,"errorMessage":"readDir %s","messagePattern":"readDir (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/fsutil/followlinks.go","lineNumber":202,"sourceCode":"\t\t\t}\n\t\t\tout = make([]gofs.DirEntry, 0)\n\t\t\treturn nil\n\t\t}\n\t\tif out == nil {\n\t\t\treturn errors.Errorf(\"expected to read parent entry %q before child %q\", root, p)\n\t\t}\n\t\tout = append(out, 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 && root != \".\" {\n\t\treturn nil, errors.Wrapf(os.ErrNotExist, \"readDir %s\", root)\n\t}\n\treturn out, nil\n}\n\nfunc containsWildcards(name string) bool {\n\tisWindows := runtime.GOOS == \"windows\"\n\tfor i := 0; i < len(name); i++ {\n\t\tch := name[i]\n\t\tif ch == '\\\\' && !isWindows {\n\t\t\ti++\n\t\t} else if ch == '*' || ch == '?' || ch == '[' {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// dedupePaths expects input as a sorted list","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/internal/fsutil/followlinks.go#L184-L220","documentation":"readDir wraps os.ErrNotExist as \"readDir %s\" when the walk produced no entries at all and the root isn't \".\". It means the directory being listed (to expand a wildcard in FollowPaths) does not exist in the FS snapshot; readSymlink treats this as not-found and resolves no targets.","triggerScenarios":"FollowLinks/NewFilterFS with a wildcard FollowPaths entry (\"glob/*\") whose parent directory is missing — readDir's walk finds no root entry, out stays nil, and ErrNotExist is wrapped.","commonSituations":"Wildcards pointing into directories absent from the snapshot (deleted build output, case-mismatched directory names, paths excluded by an outer filter FS), or following links before the directory is created in a lazy FS.","solutions":["Verify the wildcard's parent directory exists in the underlying FS before calling NewFilterFS with FollowPaths","Check for case-sensitivity or separator mismatches in the FollowPaths pattern","Treat nil resolved targets as expected: readSymlink swallows not-found, so only handle this if the wrapped error escapes another path"],"exampleFix":"// before\nFollowPaths: []string{\"output/*.json\"} // output/ missing\n// after\n// create/verify output/ in the FS snapshot first, or FollowPaths: []string{}","handlingStrategy":"validation","validationCode":"// Verify the wildcard's parent directory exists before resolving links\nparent := filepath.Dir(pattern)\nif _, err := underlying.Open(parent); err != nil {\n\treturn fmt.Errorf(\"wildcard parent missing: %s\", parent)\n}","typeGuard":null,"tryCatchPattern":"targets, err := fsutil.FollowLinks(fs, paths)\nif err != nil {\n\tif errors.Is(err, os.ErrNotExist) { return nil, nil } // dir absent: no matches\n\treturn err\n}","preventionTips":["Check case-sensitivity of directory names in FollowPaths patterns","Ensure directories are materialized in the snapshot before link resolution","Remember absent dirs resolve to zero targets, not a hard failure, in readSymlink"],"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"}