{"record":{"id":"176e39feaaeccbcc","repo":"ipfs/kubo","slug":"cannot-add-links-to-a-hamtshard-at-the-dag-pb-leve","errorCode":null,"errorMessage":"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","messagePattern":"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","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/coreapi/object.go","lineNumber":78,"sourceCode":"\t// This command operates at the dag-pb level via dagutils.Editor, which\n\t// only manipulates ProtoNode links without updating UnixFS metadata.\n\t// Only plain UnixFS Directory nodes are safe to mutate this way.\n\t// File nodes: adding links corrupts Blocksizes, content lost on read-back.\n\t// HAMTShard nodes: bitfield not updated, shard trie becomes inconsistent.\n\t// https://specs.ipfs.tech/unixfs/#pbnode-links-name\n\t// https://github.com/ipfs/kubo/issues/7190\n\tif !options.SkipUnixFSValidation {\n\t\tfsNode, err := ft.FSNodeFromBytes(basePb.Data())\n\t\tif err != nil {\n\t\t\treturn path.ImmutablePath{}, fmt.Errorf(\n\t\t\t\t\"cannot add named links to a non-UnixFS dag-pb node; \" +\n\t\t\t\t\t\"pass --allow-non-unixfs to skip validation\")\n\t\t}\n\t\tswitch fsNode.Type() {\n\t\tcase ft.TDirectory:\n\t\t\t// plain directories: safe, no link-count metadata to desync\n\t\tcase ft.THAMTShard:\n\t\t\treturn path.ImmutablePath{}, fmt.Errorf(\n\t\t\t\t\"cannot add links to a HAMTShard at the dag-pb level \" +\n\t\t\t\t\t\"(would corrupt the HAMT bitfield); use 'ipfs files' \" +\n\t\t\t\t\t\"commands instead, or pass --allow-non-unixfs to override\")\n\t\tdefault:\n\t\t\treturn path.ImmutablePath{}, fmt.Errorf(\n\t\t\t\t\"cannot add named links to a UnixFS %s node, \"+\n\t\t\t\t\t\"only Directory nodes support link addition at the dag-pb level \"+\n\t\t\t\t\t\"(see https://specs.ipfs.tech/unixfs/)\",\n\t\t\t\tfsNode.Type())\n\t\t}\n\t}\n\n\tvar createfunc func() *dag.ProtoNode\n\tif options.Create {\n\t\tcreatefunc = ft.EmptyDirNode\n\t}\n\n\te := dagutils.NewDagEditor(basePb, api.dag)","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/coreapi/object.go#L60-L96","documentation":"CoreAPI's AddLink edits dag-pb nodes directly, which is only safe for plain UnixFS directories. A HAMTShard (a hamt-shard directory created e.g. with --hamt-encoding) stores membership in a bitfield spread across its DAG; blind dag-pb link insertion would desync it and silently corrupt lookups. The library refuses and points to the 'ipfs files' API or the --allow-non-unixfs escape hatch.","triggerScenarios":"Calling PinAPI-adjacent object API AddLink (coreapi/unixfs AddLink) with SkipUnixFSValidation=false on a path whose root node is a UnixFS HAMTShard (fs.Type()==THAMTShard).","commonSituations":"Directories created with 'ipfs add --json' or 'ipfs files --hamt-encoding' become HAMT sharded; scripts that worked on plain directories using the deprecated object/patch add-link RPC start failing after re-sharding the directory.","solutions":["Use the MFS/files API (coreapi FilesAPI) to modify the directory, or CLI 'ipfs files' commands","Re-create the directory as a plain UnixFS directory if dag-pb patching is truly required","Set the --allow-non-unixfs option / SkipUnixFSValidation=true only if you accept corrupting the HAMT bitfield"],"exampleFix":"// before (RPC)\nipfs object patch add-link <hamt-dir-cid> child.txt <child-cid>\n// after\nipfs files cp /ipfs/<child-cid> /my-hamt-dir/child.txt","handlingStrategy":"validation","validationCode":"fsNode, err := ft.FSNodeFromBytes(pbNode.Data())\nif err == nil && fsNode.Type() == ft.THAMTShard {\n    // route through files/MFS API instead of AddLink\n}","typeGuard":"func isHAMTShard(n ipld.Node) bool {\n\tpb, ok := n.(*dagpb.PBNode)\n\tif !ok { return false }\n\tfsn, err := ft.FSNodeFromBytes(pb.Data())\n\treturn err == nil && fsNode.Type() == ft.THAMTShard\n}","tryCatchPattern":null,"preventionTips":["Use MFS ('ipfs files') for any directory mutation instead of legacy object patch","Check directory type (is-sharded via 'ipfs files stat') before patching","Treat object patch commands as dag-pb-level and UnixFS-plain-directory only"],"tags":["unixfs","hamt","dag-pb","deprecated-api"],"backgroundTag":"unixfs-hamt-dag-pb-unsupported-edit","analyzedSha":"329838acdfafae224582930457efe80aa217afc0","analyzedAt":"2026-09-03T18:30:52.135Z","contentChangedAt":"2026-09-03T18:30:52.135Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}