ipfs/kubo · error

cannot add named links to a UnixFS %s node, only Directory n

Error message

cannot add named links to a UnixFS %s node, only Directory nodes support link addition at the dag-pb level (see https://specs.ipfs.tech/unixfs/)

What it means

AddLink at the dag-pb level only supports plain UnixFS Directory nodes. Other UnixFS node types (File, Raw, Symlink, Metadata...) have no meaningful 'named links' semantics for link addition and dagutils.Editor cannot keep their metadata consistent. The error reports the actual node type so the caller knows what they passed.

Source

Thrown at core/coreapi/object.go:83

	// https://specs.ipfs.tech/unixfs/#pbnode-links-name
	// https://github.com/ipfs/kubo/issues/7190
	if !options.SkipUnixFSValidation {
		fsNode, err := ft.FSNodeFromBytes(basePb.Data())
		if err != nil {
			return path.ImmutablePath{}, fmt.Errorf(
				"cannot add named links to a non-UnixFS dag-pb node; " +
					"pass --allow-non-unixfs to skip validation")
		}
		switch fsNode.Type() {
		case ft.TDirectory:
			// plain directories: safe, no link-count metadata to desync
		case ft.THAMTShard:
			return path.ImmutablePath{}, fmt.Errorf(
				"cannot add links to a HAMTShard at the dag-pb level " +
					"(would corrupt the HAMT bitfield); use 'ipfs files' " +
					"commands instead, or pass --allow-non-unixfs to override")
		default:
			return path.ImmutablePath{}, fmt.Errorf(
				"cannot add named links to a UnixFS %s node, "+
					"only Directory nodes support link addition at the dag-pb level "+
					"(see https://specs.ipfs.tech/unixfs/)",
				fsNode.Type())
		}
	}

	var createfunc func() *dag.ProtoNode
	if options.Create {
		createfunc = ft.EmptyDirNode
	}

	e := dagutils.NewDagEditor(basePb, api.dag)

	err = e.InsertNodeAtPath(ctx, name, childNd, createfunc)
	if err != nil {
		return path.ImmutablePath{}, err
	}

View on GitHub (pinned to 329838acdf)

Solutions

  1. Pass the directory CID/path, not a file or other node
  2. Use 'ipfs files' / MFS API for modifying UnixFS content
  3. Use --allow-non-unixfs to bypass validation if you explicitly want raw dag-pb editing

Example fix

// before
ipfs object patch add-link QmFileCid name.txt QmChild
// after: resolve to the directory first
ipfs object patch add-link QmDirCid name.txt QmChild
Defensive patterns

Strategy: validation

Validate before calling

fsNode, err := ft.FSNodeFromBytes(pbNode.Data())
if err != nil || fsNode.Type() != ft.TDirectory {
    // not a plain directory: refuse dag-pb link addition
}

Type guard

func isPlainUnixFSDirectory(n ipld.Node) bool {
	pb, ok := n.(*dagpb.PBNode)
	if !ok { return false }
	fsn, err := ft.FSNodeFromBytes(pb.Data())
	return err == nil && fsn.Type() == ft.TDirectory
}

Prevention

When it happens

Trigger: AddLink on a resolved path whose root dag-pb node is a UnixFS node of any type other than TDirectory or THAMTShard (e.g. a file or raw leaf).

Common situations: Passing a file CID instead of a directory CID to object patch add-link; path resolution landing on an inner file node due to a typo'd path.

Related errors


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