{"record":{"id":"119daebe7b351430","repo":"ipfs/kubo","slug":"cannot-remove-links-from-a-hamtshard-at-the-dag-pb","errorCode":null,"errorMessage":"cannot remove links from a HAMTShard at the dag-pb level (would corrupt the HAMT bitfield); use 'ipfs files rm' instead, or pass --allow-non-unixfs to override","messagePattern":"cannot remove links from a HAMTShard at the dag-pb level \\(would corrupt the HAMT bitfield\\); use 'ipfs files rm' instead, or pass --allow-non-unixfs to override","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/coreapi/object.go","lineNumber":146,"sourceCode":"\tbasePb, ok := baseNd.(*dag.ProtoNode)\n\tif !ok {\n\t\treturn path.ImmutablePath{}, dag.ErrNotProtobuf\n\t}\n\n\t// Same validation as AddLink: dagutils.Editor operates at the dag-pb\n\t// level and cannot update UnixFS metadata (HAMT bitfields, Blocksizes).\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 remove links from 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 remove links from a HAMTShard at the dag-pb level \" +\n\t\t\t\t\t\"(would corrupt the HAMT bitfield); use 'ipfs files rm' \" +\n\t\t\t\t\t\"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 remove links from a UnixFS %s node, \"+\n\t\t\t\t\t\"only Directory nodes support link removal 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\te := dagutils.NewDagEditor(basePb, api.dag)\n\n\terr = e.RmLink(ctx, link)\n\tif err != nil {\n\t\treturn path.ImmutablePath{}, err\n\t}","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/coreapi/object.go#L128-L164","documentation":"Mirror of the AddLink HAMTShard case for removal: deleting a link from a HAMTShard via dag-pb editing would leave the HAMT bitfield and bucket structure inconsistent, breaking future lookups. RmLink refuses and directs to 'ipfs files rm' or the explicit override.","triggerScenarios":"RmLink on a path resolving to a UnixFS HAMTShard root with SkipUnixFSValidation=false.","commonSituations":"Scripts removing entries from sharded MFS root directories via the old object patch rm-link RPC after the directory was converted to a HAMT shard.","solutions":["Use MFS API or 'ipfs files rm /path/in/sharded-dir'","Convert the directory back to a plain directory if legacy patching is required","Pass --allow-non-unixfs only with full awareness of bitfield corruption"],"exampleFix":"// before\nipfs object patch rm-link <hamt-dir-cid> child.txt\n// after\nipfs files rm /my-hamt-dir/child.txt","handlingStrategy":"validation","validationCode":"fsNode, err := ft.FSNodeFromBytes(pbNode.Data())\nif err == nil && fsNode.Type() == ft.THAMTShard {\n    // use 'ipfs files rm' / MFS instead of RmLink\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 && fsn.Type() == ft.THAMTShard\n}","tryCatchPattern":null,"preventionTips":["Never use object patch rm-link on sharded directories","Use MFS operations which handle HAMT re-sharding correctly","Detect sharding early with 'ipfs files stat' (is-sharded)"],"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"}