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

  1. Run `ipfs block get <cid>` while the daemon is running to fetch and store the block locally, then retry chroot.
  2. Pin the target CID first so GC cannot remove it again (`ipfs pin add <cid>` before stopping the daemon).
  3. 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

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


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