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
- Upgrade the go-ipfs-api / boxo client and the kubo daemon to matching, current versions so both encode nodes identically.
- 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).
- 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.
- 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
- Keep go-ipfs-api/boxo and the kubo daemon on matching versions
- Pass explicit MhType and Codec options instead of relying on defaults on either side
- Avoid mixing custom IPLD codecs across library versions between client and daemon
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
- http api returned no error and no results
- unknowm mhType %d
- msg (server-provided error message)
- got unexpected number of keys back
- Name.Resolve: depth other than 1 or %d not supported
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/63af39706002224a.
Report an issue: GitHub.