{"record":{"id":"a1fa9641a0adaf9d","repo":"ipfs/kubo","slug":"expected-at-least-one-child-dir-got-none","errorCode":null,"errorMessage":"expected at least one child dir, got none","messagePattern":"expected at least one child dir, got none","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/coreunix/add.go","lineNumber":345,"sourceCode":"\troot = rootdir\n\n\terr = root.Flush()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// if adding a file without wrapping, swap the root to it (when adding a\n\t// directory, mfs root is the directory)\n\t_, dir := file.(files.Directory)\n\tvar name string\n\tif !dir {\n\t\tchildren, err := rootdir.ListNames(adder.ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif len(children) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"expected at least one child dir, got none\")\n\t\t}\n\n\t\t// Replace root with the first child\n\t\tname = children[0]\n\t\troot, err = rootdir.Child(name)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\terr = mr.Close()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnd, err := root.GetNode()\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/coreunix/add.go#L327-L363","documentation":"When AddAllAndPin finishes importing a root that is a directory, it tries to unwrap a single-child root so the final CID addresses the child directly (flattening redundant single-entry directory wrappers). If ListNames on the root directory returns zero children, it errors instead of producing a CID for an empty directory wrapper it cannot flatten. In practice this surfaces when the root directory contains no entries at all.","triggerScenarios":"Calling Adder.AddAllAndPin on a files.Directory root that ends up with no named children — e.g. files.NewMapFile(\"\", \"\", map[string]files.Node{}) or a SerialFile walk over an empty directory — where the flattening path ListNames returns an empty list.","commonSituations":"Importing an empty directory via ipfs add -r; constructing map/slice file roots programmatically and forgetting to add entries; filtering out all entries (e.g. hidden-file filters with everything excluded) before adding.","solutions":["Add at least one entry to the directory root before calling AddAllAndPin","If you intentionally want an empty-directory CID, use a different API path (e.g. construct the UnixFS dir node directly or use files/MFS APIs) rather than AddAllAndPin","Check hidden-file filters (.hide options) that may be excluding every entry","Verify the source directory passed to files.NewSerialFile is not empty"],"exampleFix":"// before: empty root\nempty := files.NewMapFile(\"\", \"\", map[string]files.Node{})\nadder.AddAllAndPin(ctx, empty, true) // expected at least one child dir, got none\n// after: include at least one child\nentry := files.NewBytesFile([]byte(\"hello\"))\nroot := files.NewMapFile(\"\", \"\", map[string]files.Node{\"hello.txt\": entry})\nadder.AddAllAndPin(ctx, root, true)","handlingStrategy":"validation","validationCode":"names, err := rootdir.ListNames(ctx)\nif err != nil { return err }\nif len(names) == 0 {\n\treturn errors.New(\"refusing to AddAllAndPin an empty directory root\")\n}","typeGuard":null,"tryCatchPattern":"if err := adder.AddAllAndPin(ctx, root, true); err != nil {\n\tif strings.Contains(err.Error(), \"expected at least one child dir\") {\n\t\t// fall back to constructing an empty-dir UnixFS node directly\n\t}\n\treturn err\n}","preventionTips":["Check the source directory is non-empty before ipfs add -r","Re-review hidden-file filter settings that can exclude all entries","When constructing files.NewMapFile roots, assert the map has at least one entry","Decide explicitly how empty-directory imports should be represented and use a dedicated API if needed"],"tags":["unixfs","add","empty-directory","importer"],"backgroundTag":"empty-directory-root","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"}