ipfs/kubo · error

reading new root block: %w

Error message

reading new root block: %w

What it means

When the new root block does exist locally, `ipfs files chroot` reads it from the blockstore; a failure at bs.Get (datastore I/O error, block vanished between Has and Get) is wrapped with this message. The block is then decoded as protobuf/FSNode, with separate errors for non-dag-pb or non-directory payloads.

Source

Thrown at core/commands/files.go:1784

		// 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 {
				return fmt.Errorf("new root must be a directory, got %s", fsNode.Type())
			}
		}

		// Get old root for display (if exists)
		var oldRootStr string
		oldRootBytes, err := localDS.Get(req.Context, node.FilesRootDatastoreKey)
		if err == nil {

View on GitHub (pinned to 329838acdf)

Solutions

  1. Verify the datastore health (check daemon logs, run `ipfs repo fsck`-style checks or inspect disk errors) and retry.
  2. Re-fetch the block (`ipfs block get <cid>` with the daemon up) to restore/replace a corrupted copy.
  3. Confirm the CID names a UnixFS directory node (`ipfs dag get <cid>` / `ipfs files stat` on an added dir) before chrooting.

Example fix

# before
ipfs files chroot --confirm <cid-of-raw-block>   # reading/decoding fails
# after
CID=$(ipfs add -Q --only-hash mydir/ )   # ensures UnixFS directory DAG
ipfs block get "$CID" > /dev/null && ipfs shutdown
ipfs files chroot --confirm "$CID"
Defensive patterns

Strategy: retry

Validate before calling

ipfs dag get "$CID" > /dev/null 2>&1 || echo "not a readable dag-pb node"

Type guard

func isDirNode(c cid.Cid) bool { return c.Type() == cid.DagProtobuf }

Try / catch

if _, err := bs.Get(ctx, cid); err != nil {
    // transient (GC race) -> re-fetch block via block get, then retry chroot
}

Prevention

When it happens

Trigger: Datastore read failure during bs.Get (corruption, disk error, concurrent GC removing the block between Has and Get); the CID pointing to a raw/other-codec block instead of a UnixFS dag-pb node (raised by the subsequent DecodeProtobuf/FSNodeFromBytes wraps in the same region).

Common situations: A concurrent `ipfs repo gc` deleting unpinned blocks while chroot runs; damaged flatfs/badger shards; passing a CID of a raw block or file where a directory node is required.

Related errors


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