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

  1. Check each intermediate path component with `ipfs files stat` and ensure parents are directories.
  2. Re-run the command if the tree was being modified concurrently; serialize MFS mutations.
  3. 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

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


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/9bcac5766a543fe4. Report an issue: GitHub.