ipfs/kubo · error
expected *mfs.Directory, didn't get it. This is likely a rac
Error message
expected *mfs.Directory, didn't get it. This is likely a race condition
What it means
createCmdFetchParent (used by `files mkdir`, `files touch`, `files rm` and similar) looks up the parent of a target path and asserts it is *mfs.Directory. If the parent node is not a directory — or the node type changed concurrently between lookup and assertion — the assertion fails and this sentinel error is returned instead of panicking.
Source
Thrown at core/commands/files.go:1604
return "", fmt.Errorf("paths must start with a leading slash")
}
cleaned := gopath.Clean(p)
if p[len(p)-1] == '/' && p != "/" {
cleaned += "/"
}
return cleaned, nil
}
func getParentDir(root *mfs.Root, dir string) (*mfs.Directory, error) {
parent, err := mfs.Lookup(root, dir)
if err != nil {
return nil, err
}
pdir, ok := parent.(*mfs.Directory)
if !ok {
return nil, errors.New("expected *mfs.Directory, didn't get it. This is likely a race condition")
}
return pdir, nil
}
var filesChmodCmd = &cmds.Command{
Status: cmds.Experimental,
Helptext: cmds.HelpText{
Tagline: "Change optional POSIX mode permissions",
ShortDescription: `
The mode argument must be specified in Unix numeric notation.
$ ipfs files chmod 0644 /foo
$ ipfs files stat /foo
...
Type: file
Mode: -rw-r--r-- (0644)
...
`,View on GitHub (pinned to 329838acdf)
Solutions
- Check each intermediate path component with `ipfs files stat` and ensure parents are directories.
- Re-run the command if the tree was being modified concurrently; serialize MFS mutations.
- Create missing parent directories first (`ipfs files mkdir -p /a/b`) so the parent is guaranteed to be a directory.
Example fix
// before: racy parent
ipfs files touch /data/${i}/file # /data/${i} may be replaced mid-run
// after: ensure parents exist and are directories first
ipfs files mkdir -p /data/${i}
ipfs files touch /data/${i}/file Defensive patterns
Strategy: validation
Validate before calling
for c in $(echo "${P%/*}" | tr '/' ' '); do [ "$(ipfs files stat --format='<type>' "/$c")" = "directory" ] || echo "parent not a directory"; done Type guard
if d, ok := parent.(*mfs.Directory); ok { return d, nil }
return nil, fmt.Errorf("parent of %s is not a directory", path) Try / catch
pdir, err := createCmdFetchParent(node, path)
if err != nil {
if strings.Contains(err.Error(), "expected *mfs.Directory") { /* re-stat parents, retry once */ }
return err
} Prevention
- Run `files mkdir -p` for the parent chain before creating children
- Avoid concurrent rm/mv on ancestor directories of in-flight operations
- Stat intermediate components when paths are user-supplied
When it happens
Trigger: Running a command whose parent path component is actually a file (e.g. `ipfs files mkdir /a/file/sub` where /a/file is a file); a concurrent `files rm`/`files mv` replacing the parent between lookup and assertion; a write/create command targeting a path whose intermediate entry was swapped.
Common situations: Scripts racing with each other on the same MFS tree; typos where a path component names a file; automation that renames directories while another job creates entries inside them.
Related errors
- expected *mfs.File, didn't get it. This is likely a race con
- assets: could load Asset '%s': %s
- pin check failed: %w
- r.String()
- ipfs api address could not be found
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/9bcac5766a543fe4.
Report an issue: GitHub.