ipfs/kubo · error

cids didn't match - local %s, remote %s

Error message

cids didn't match - local %s, remote %s

What it means

httpNodeAdder.add builds the node locally, sends its raw bytes/links to the daemon's dag/put, and then checks that the CID the server reports back equals the CID the client computed. If they differ, the serialized bytes the daemon produced do not match what the client intended, so the client fails instead of silently returning a differently-addressed node.

Source

Thrown at client/rpc/dag.go:83

	// 'format' got replaced by 'cid-codec' in https://github.com/ipfs/interface-go-ipfs-core/pull/80
	// but we still support it here for backward-compatibility with use of CIDv0
	format := ""
	if prefix.Version == 0 {
		cidCodec = ""
		format = "v0"
	}

	stat, err := api.core().Block().Put(ctx, bytes.NewReader(nd.RawData()),
		options.Block.Hash(prefix.MhType, prefix.MhLength),
		options.Block.CidCodec(cidCodec),
		options.Block.Format(format),
		options.Block.Pin(pin))
	if err != nil {
		return err
	}
	if !stat.Path().RootCid().Equals(c) {
		return fmt.Errorf("cids didn't match - local %s, remote %s", c.String(), stat.Path().RootCid().String())
	}
	return nil
}

func (api *httpNodeAdder) addMany(ctx context.Context, nds []format.Node, pin bool) error {
	for _, nd := range nds {
		// TODO: optimize
		if err := api.add(ctx, nd, pin); err != nil {
			return err
		}
	}
	return nil
}

func (api *HttpDagServ) AddMany(ctx context.Context, nds []format.Node) error {
	return (*httpNodeAdder)(api).addMany(ctx, nds, false)
}

View on GitHub (pinned to 329838acdf)

Solutions

  1. Upgrade the go-ipfs-api / boxo client and the kubo daemon to matching, current versions so both encode nodes identically.
  2. Check the Codec, MhType, and CID version options you pass to Dag().Add; make sure they match what you intend (e.g. cid.DagProtobuf vs cid.Raw).
  3. Log both CIDs from the error message; if local is CIDv0 and remote is CIDv1 (or hashes differ), align the MhType/Version options rather than ignoring the mismatch.
  4. If using custom IPLD codecs, verify both sides link the same go-ipld-prime / codec library versions.

Example fix

// before
nd, _ := format.NewNode(ctx, data)
api.Dag().Add(ctx, nd, opts.Dag.MhType(0x22)) // daemon defaults differ
// after
nd, _ := format.NewNode(ctx, data)
api.Dag().Add(ctx, nd, opts.Dag.MhType(mh.SHA2_256).Codec(cid.DagProtobuf)) // explicit, matches daemon
Defensive patterns

Strategy: try-catch

Try / catch

nd, err := format.NewNode(ctx, data)
if err != nil { return err }
out, err := api.Dag().Add(ctx, nd, opts)
if err != nil {
    var cidErr *strings.Reader // message: "cids didn't match - local %s, remote %s"
    if strings.Contains(err.Error(), "cids didn't match") {
        return fmt.Errorf("dag add: client/daemon encoding mismatch (%w); check go-ipfs-api and kubo versions and explicit MhType/Codec opts", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling api.Dag().Add with a locally built format.Node whose re-serialization on the server yields a different root CID — e.g. mismatched Codec/MhType/Version options between client and server, or a client and daemon on incompatible boxo/go-cid versions that encode the same logical node differently.

Common situations: Kubo daemon and go-ipfs-api/boxo client version skew (codec defaults changed), specifying opts.Dag.Codec or MhType that the daemon applies differently, or custom IPLD codecs where node encoding differs between library versions.

Related errors


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