ipfs/kubo · error

not a directory: %s

Error message

not a directory: %s

What it means

unlinkNodeIfExists resolves the parent of the target path and asserts it is an *mfs.Directory. If the resolved parent is some other node type (e.g. an MFS file used as an intermediate path component), it returns "not a directory: <dir>". This is raised from `ipfs files cp --force` when trying to unlink an existing destination.

Source

Thrown at core/commands/files.go:651

	if p, err := path.NewPathFromURI(arg); err == nil {
		return p.String(), nil
	}
	return checkPath(arg)
}

func unlinkNodeIfExists(node *core.IpfsNode, path string) error {
	dir, name := gopath.Split(path)
	parent, err := mfs.Lookup(node.FilesRoot, dir)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return nil
		}
		return err
	}

	pdir, ok := parent.(*mfs.Directory)
	if !ok {
		return fmt.Errorf("not a directory: %s", dir)
	}

	// Attempt to unlink if child is a file, ignore error since
	// we are only concerned with unlinking an existing file.
	child, err := pdir.Child(name)
	if err != nil {
		return nil // no child file, nothing to unlink
	}

	if child.Type() != mfs.TFile {
		return fmt.Errorf("not a file: %s", path)
	}

	return pdir.Unlink(name)
}

type filesLsOutput struct {
	Entries []mfs.NodeListing

View on GitHub (pinned to 329838acdf)

Solutions

  1. Inspect the path components: `ipfs files stat /file.txt` to find the non-directory component
  2. Remove or rename the offending file: `ipfs files rm /file.txt` and `ipfs files mkdir` a directory in its place
  3. Re-run `ipfs files cp --force` with a corrected dst path
  4. Use `ipfs files ls /` to verify the intended directory tree structure

Example fix

// before
ipfs files cp --force /ipfs/Qm... /notes.txt/todo
// error: not a directory: /notes.txt

// after
ipfs files mkdir /notes
ipfs files cp --force /ipfs/Qm... /notes/todo
Defensive patterns

Strategy: validation

Validate before calling

// every dst component except the last must be a directory
for _, dir := range allParents(dst) {
    if t := pathType(dir); t != "directory" {
        return fmt.Errorf("%s is a %s, not a directory", dir, t)
    }
}

Type guard

func isMFSDirectory(p string) bool {
    out, err := run("ipfs", "files", "stat", p)
    return err == nil && strings.Contains(out, "directory")
}

Try / catch

if err := ipfsFilesCpForce(src, dst); err != nil {
    if strings.Contains(err.Error(), "not a directory") {
        // fix the path: replace the file component with a directory
    }
}

Prevention

When it happens

Trigger: Running `ipfs files cp --force <src> /file.txt/child` where /file.txt exists but is a file, not a directory, so resolving the parent returns a non-directory MFS node.

Common situations: dst path with a file as an intermediate component; typo'd dst where a filename was meant to be a folder name; MFS tree corrupted so a directory path resolves to a file node.

Related errors


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