{"record":{"id":"df7e5498acb71c21","repo":"juicedata/juicefs","slug":"readdir-s","errorCode":null,"errorMessage":"readdir %s","messagePattern":"readdir (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/object/nfs.go","lineNumber":271,"sourceCode":"\t}\n\tif fi.IsDir() {\n\t\tif key != \"\" && !strings.HasSuffix(key, \"/\") {\n\t\t\tff.key += \"/\"\n\t\t}\n\t\tff.size = 0\n\t}\n\treturn ff\n}\n\nfunc (n *nfsStore) readDirSorted(ctx context.Context, dir string, followLink bool) ([]*nfsEntry, error) {\n\to, err := n.Head(ctx, strings.TrimSuffix(dir, \"/\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdirname := o.Key()\n\tentries, err := n.target.ReadDirPlus(dirname)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"readdir %s\", dirname)\n\t}\n\tnfsEntries := make([]*nfsEntry, 0, len(entries))\n\tfor _, e := range entries {\n\t\tisSymlink := e.Attr.Attr.Type == nfs.NF3Lnk\n\t\tif e.IsDir() {\n\t\t\tnfsEntries = append(nfsEntries, &nfsEntry{e, e.Name() + dirSuffix, nil, false})\n\t\t} else if isSymlink && followLink {\n\t\t\t// follow symlink\n\t\t\tsrc, err := n.Readlink(path.Join(dirname, e.Name()))\n\t\t\tif err != nil {\n\t\t\t\tnfsEntries = append(nfsEntries, &nfsEntry{e, e.Name(), nil, true})\n\t\t\t\tlogger.Errorf(\"readlink %s: %s\", e.Name(), err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsrcPath := path.Clean(path.Join(dirname, src))\n\t\t\tfi, _, err := n.target.Lookup(srcPath)\n\t\t\tif err != nil {\n\t\t\t\tnfsEntries = append(nfsEntries, &nfsEntry{e, e.Name(), nil, true})","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/object/nfs.go#L253-L289","documentation":"readDirSorted in nfs.go wraps a failure from n.target.ReadDirPlus(dirname) with `readdir %s`. It means listing a directory (prefix) on the NFS target failed while serving List. The wrapped underlying error distinguishes ENOENT, ENOTDIR, EACCES, and NFS protocol/mount failures.","triggerScenarios":"Calling List with a prefix whose directory does not exist on the export, the key resolves to a regular file (ENOTDIR), read permission is denied, or the NFS connection/mount fails during ReadDirPlus.","commonSituations":"Listing a prefix after its directory was deleted, wrong export root configured so the prefix maps outside the tree, permission changes on directories, or stale NFS mounts after network/server disruption.","solutions":["Verify the directory exists on the NFS export for the given prefix (or accept the missing-prefix case in your sync logic)","Ensure the key path is a directory, not a file (trailing '/' semantics)","Check directory read/execute permissions for the client user","Remount or health-check the NFS export if the error is a stale-handle/connection failure"],"exampleFix":"// before\nfor _, it := range mustList(nfsStore, \"data/2024/missing/\") {}\n// after\nits, err := nfsStore.List(ctx, \"data/2024/missing/\", \"\")\nif err != nil { log.Warnf(\"skip missing prefix: %v\", err); return }","handlingStrategy":"try-catch","validationCode":"if fi, err := os.Stat(dirname); err != nil || !fi.IsDir() { return errSkipPrefix }","typeGuard":null,"tryCatchPattern":"it, err := store.List(ctx, prefix, \"/\")\nif err != nil {\n    if errors.Is(err, os.ErrNotExist) { return nil } // treat missing prefix as empty\n    return err\n}","preventionTips":["Treat missing prefixes as empty listings in sync/list callers","Verify the prefix maps to a directory under the configured export root","Ensure directory read+execute permissions for the client user","Monitor and remount stale NFS connections before large listings"],"tags":["nfs","readdir","object-storage","list"],"backgroundTag":"directory-not-found","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}