ipfs/kubo · error
new root %s does not exist locally; fetch it first with 'ipf
Error message
new root %s does not exist locally; fetch it first with 'ipfs block get'
What it means
`ipfs files chroot` requires the new root to be present in the local blockstore. If bs.Has reports the block missing and the CID is not the hardcoded empty-directory CID (which boxo can always materialize), the command returns this error, refusing to chroot to content it cannot verify.
Source
Thrown at core/commands/files.go:1776
repo, err := fsrepo.Open(cfgRoot)
if err != nil {
return fmt.Errorf("opening repo (is the daemon running?): %w", err)
}
defer repo.Close()
localDS := repo.Datastore()
bs := bstore.NewBlockstore(localDS)
// Check new root exists locally and is a directory
hasBlock, err := bs.Has(req.Context, newRootCid)
if err != nil {
return fmt.Errorf("checking if new root exists: %w", err)
}
if !hasBlock {
// Special case: empty dir is always available (hardcoded in boxo)
emptyDirCid := ft.EmptyDirNode().Cid()
if !newRootCid.Equals(emptyDirCid) {
return fmt.Errorf("new root %s does not exist locally; fetch it first with 'ipfs block get'", enc.Encode(newRootCid))
}
}
// Validate it's a directory (not a file)
if hasBlock {
blk, err := bs.Get(req.Context, newRootCid)
if err != nil {
return fmt.Errorf("reading new root block: %w", err)
}
pbNode, err := dag.DecodeProtobuf(blk.RawData())
if err != nil {
return fmt.Errorf("new root is not a valid dag-pb node: %w", err)
}
fsNode, err := ft.FSNodeFromBytes(pbNode.Data())
if err != nil {
return fmt.Errorf("new root is not a valid UnixFS node: %w", err)
}
if fsNode.Type() != ft.TDirectory && fsNode.Type() != ft.THAMTShard {View on GitHub (pinned to 329838acdf)
Solutions
- Run `ipfs block get <cid>` while the daemon is running to fetch and store the block locally, then retry chroot.
- Pin the target CID first so GC cannot remove it again (`ipfs pin add <cid>` before stopping the daemon).
- Use the empty-directory CID (or omit the argument) if the intent is to reset to an empty root.
Example fix
# before ipfs files chroot --confirm bafybei... # not in blockstore # after ipfs pin add bafybei... # with daemon running; also fetches ipfs shutdown ipfs files chroot --confirm bafybei...
Defensive patterns
Strategy: fallback
Validate before calling
ipfs block stat "$CID" 2>/dev/null || echo "block not local; fetch with: ipfs block get $CID"
Try / catch
if out, err := run("ipfs","files","chroot","--confirm",cid); err != nil && strings.Contains(out, "does not exist locally") {
run("ipfs","block","get",cid)
err = run("ipfs","files","chroot","--confirm",cid)
} Prevention
- Pin the target root before GC can evict it
- Run `ipfs block get <cid>` as an explicit pre-fetch step in automation
- On fresh nodes, remember only the empty dir is guaranteed present
When it happens
Trigger: Chrooting to any UnixFS directory CID that was never fetched to this node; the block existed earlier but was removed by garbage collection; a typo in the CID that resolves to a different, unfetched node.
Common situations: Following documentation/examples that use CIDs from another machine; after `ipfs repo gc` pruned unpinned blocks; running chroot on a fresh node whose blockstore only holds the empty dir.
Related errors
- checking if new root exists: %w
- inline-limit %d exceeds maximum allowed size of %d bytes
- aborted: %s
- some blocks not removed
- invalid format string: %q
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/78478071533e605e.
Report an issue: GitHub.