juicedata/juicefs · error
inode %d is not a directory
Error message
inode %d is not a directory
What it means
Thrown while building an absolute path from an inode (path resolution walking up via attr.Parent): the inode exists but its type is not a directory, so it cannot be treated as a directory entry during the parent walk. The metadata engine enforces the invariant that every node in the ancestor chain of a path must be a directory.
Source
Thrown at pkg/meta/base.go:2367
func (m *baseMeta) GetPaths(ctx Context, inode Ino) []string {
if inode == RootInode {
return []string{"/"}
}
if inode == TrashInode {
return []string{"/.trash"}
}
outside := "path not shown because it's outside of the mounted root"
getDirPath := func(ino Ino) (string, error) {
var names []string
var attr Attr
for ino != RootInode && ino != m.root {
if st := m.en.doGetAttr(ctx, ino, &attr); st != 0 {
return "", fmt.Errorf("getattr inode %d: %s", ino, st)
}
if attr.Typ != TypeDirectory {
return "", fmt.Errorf("inode %d is not a directory", ino)
}
var entries []*Entry
if st := m.en.doReaddir(ctx, attr.Parent, 0, &entries, -1); st != 0 {
return "", fmt.Errorf("readdir inode %d: %s", ino, st)
}
var name string
for _, e := range entries {
if e.Inode == ino {
name = string(e.Name)
break
}
}
if attr.Parent == RootInode && ino == TrashInode {
name = TrashName
}
if name == "" {
return "", fmt.Errorf("entry %d/%d not found", attr.Parent, ino)
}View on GitHub (pinned to c9a67b23e8)
Solutions
- Refresh the inode: re-resolve the path from the current directory tree instead of using a stale cached inode
- Check the inode type first (getattr) and only pass directory inodes to path resolution
- If it occurs without stale handles, run fsck / verify the metadata engine for corruption
Example fix
// before
entries, err := fs.ReadDir(ino) // ino from stale cache
// after
attr, err := fs.GetAttr(ino)
if err == nil && attr.Typ == TypeDirectory {
entries, err = fs.ReadDir(ino)
} Defensive patterns
Strategy: validation
Validate before calling
attr, st := meta.GetAttr(ctx, ino)
if st != 0 { return st }
if attr.Typ != meta.TypeDirectory { return errors.New("not a directory") }
// proceed with path resolution Type guard
func isDir(attr meta.Attr) bool { return attr.Typ == meta.TypeDirectory } Prevention
- Never cache inodes across unlink/rename operations
- Re-stat the inode before path-based operations after long-lived handles
- Run fsck periodically to catch dentry/parent inconsistencies
When it happens
Trigger: Calling path-building/resolution APIs (e.g. GetPath / getPath via doGetAttr+doReaddir walk) with an inode whose Attr.Typ is not TypeDirectory; typically happens when a stale or corrupted inode is passed, or when the inode was concurrently removed and recreated as a file.
Common situations: Applications holding cached inode handles across a delete+recreate where the new inode is a regular file; operating on the trash or subdirectory-mount root with stale inodes; metadata corruption or mismatched metadata backend state.
Related errors
AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06).
Data as JSON: /api/errors/67aa783d20f8acdb.
Report an issue: GitHub.