juicedata/juicefs · error

readdir %s

Error message

readdir %s

What it means

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.

Source

Thrown at pkg/object/nfs.go:271

	}
	if fi.IsDir() {
		if key != "" && !strings.HasSuffix(key, "/") {
			ff.key += "/"
		}
		ff.size = 0
	}
	return ff
}

func (n *nfsStore) readDirSorted(ctx context.Context, dir string, followLink bool) ([]*nfsEntry, error) {
	o, err := n.Head(ctx, strings.TrimSuffix(dir, "/"))
	if err != nil {
		return nil, err
	}
	dirname := o.Key()
	entries, err := n.target.ReadDirPlus(dirname)
	if err != nil {
		return nil, errors.Wrapf(err, "readdir %s", dirname)
	}
	nfsEntries := make([]*nfsEntry, 0, len(entries))
	for _, e := range entries {
		isSymlink := e.Attr.Attr.Type == nfs.NF3Lnk
		if e.IsDir() {
			nfsEntries = append(nfsEntries, &nfsEntry{e, e.Name() + dirSuffix, nil, false})
		} else if isSymlink && followLink {
			// follow symlink
			src, err := n.Readlink(path.Join(dirname, e.Name()))
			if err != nil {
				nfsEntries = append(nfsEntries, &nfsEntry{e, e.Name(), nil, true})
				logger.Errorf("readlink %s: %s", e.Name(), err)
				continue
			}
			srcPath := path.Clean(path.Join(dirname, src))
			fi, _, err := n.target.Lookup(srcPath)
			if err != nil {
				nfsEntries = append(nfsEntries, &nfsEntry{e, e.Name(), nil, true})

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Verify the directory exists on the NFS export for the given prefix (or accept the missing-prefix case in your sync logic)
  2. Ensure the key path is a directory, not a file (trailing '/' semantics)
  3. Check directory read/execute permissions for the client user
  4. Remount or health-check the NFS export if the error is a stale-handle/connection failure

Example fix

// before
for _, it := range mustList(nfsStore, "data/2024/missing/") {}
// after
its, err := nfsStore.List(ctx, "data/2024/missing/", "")
if err != nil { log.Warnf("skip missing prefix: %v", err); return }
Defensive patterns

Strategy: try-catch

Validate before calling

if fi, err := os.Stat(dirname); err != nil || !fi.IsDir() { return errSkipPrefix }

Try / catch

it, err := store.List(ctx, prefix, "/")
if err != nil {
    if errors.Is(err, os.ErrNotExist) { return nil } // treat missing prefix as empty
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/df7e5498acb71c21. Report an issue: GitHub.