hyperledger/fabric · critical

failed to unmarshal consensus metadata

Error message

failed to unmarshal consensus metadata

What it means

HandleChain (orderer/consensus/etcdraft/consenter.go:122) unmarshals the channel's consensus metadata from the shared config into an etcdraft.ConfigMetadata proto. This error wraps an unmarshal failure, meaning the bytes stored in the channel config's ConsensusType.Metadata are not a valid ConfigMetadata protobuf. The orderer cannot start the raft chain for the channel.

Source

Thrown at orderer/consensus/etcdraft/consenter.go:122

		certAsDER, err := pemToDER(cst.GetServerTlsCert(), nodeID, "server", c.Logger)
		if err != nil {
			return 0, err
		}

		if crypto.CertificatesWithSamePublicKey(thisNodeCertAsDER, certAsDER) == nil {
			return nodeID, nil
		}
	}

	c.Logger.Warning("Could not find", string(c.Cert), "among", serverCertificates)
	return 0, cluster.ErrNotInChannel
}

// HandleChain returns a new Chain instance or an error upon failure
func (c *Consenter) HandleChain(support consensus.ConsenterSupport, metadata *common.Metadata) (consensus.Chain, error) {
	m := &etcdraft.ConfigMetadata{}
	if err := proto.Unmarshal(support.SharedConfig().ConsensusMetadata(), m); err != nil {
		return nil, errors.Wrap(err, "failed to unmarshal consensus metadata")
	}

	if m.GetOptions() == nil {
		return nil, errors.New("etcdraft options have not been provided")
	}

	isMigration := (metadata == nil || len(metadata.GetValue()) == 0) && (support.Height() > 1)
	if isMigration {
		c.Logger.Debugf("Block metadata is nil at block height=%d, it is consensus-type migration", support.Height())
	}

	// determine raft replica set mapping for each node to its id
	// for newly started chain we need to read and initialize raft
	// metadata by creating mapping between conseter and its id.
	// In case chain has been restarted we restore raft metadata
	// information from the recently committed block meta data
	// field.
	blockMetadata, err := ReadBlockMetadata(metadata, m)

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Regenerate the channel config so consensus metadata is a properly marshaled etcdraft.ConfigMetadata protobuf (as configtxgen/configtxlator produce)
  2. Verify the consensus type is actually etcdraft and the metadata type matches it
  3. Compare metadata bytes against a known-good configtxgen output using configtxlator decode
  4. Recreate the genesis block and reset the channel if config was hand-crafted

Example fix

// before: embedding JSON in config
Metadata: []byte(`{"Consenters":[...]}`)
// after
m := &etcdraft.ConfigMetadata{...}
Metadata, _ := proto.Marshal(m) // configtxgen handles this in Channel.Consensus.Metadata
Defensive patterns

Strategy: validation

Validate before calling

m := &etcdraft.ConfigMetadata{}
if err := proto.Unmarshal(sharedConfig.ConsensusMetadata(), m); err != nil {
    return fmt.Errorf("channel metadata is not a valid ConfigMetadata proto: %w", err)
}

Type guard

func isValidConfigMetadata(raw []byte) bool {
    m := &etcdraft.ConfigMetadata{}
    return proto.Unmarshal(raw, m) == nil && len(m.GetConsenters()) > 0
}

Prevention

When it happens

Trigger: Orderer startup or chain creation where support.SharedConfig().ConsensusMetadata() returns bytes that fail proto.Unmarshal into etcdraft.ConfigMetadata.

Common situations: Channel created with consensus metadata left as raw JSON or empty/garbage bytes instead of marshaled ConfigMetadata; a migration or config edit wrote the wrong protobuf type; tooling that JSON-encoded the metadata instead of protobuf-marshaling it.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/02e541c2156154dc. Report an issue: GitHub.