hyperledger/fabric · error

duplicate consenter: server cert: %s, client cert: %s

Error message

duplicate consenter: server cert: %s, client cert: %s

What it means

MetadataHasDuplication tracks every server and client TLS certificate seen across consenters; if any certificate (server or client, byte-identical) appears for two consenters, the metadata is rejected. Duplicate certs would map two raft nodes to the same TLS identity, breaking leader election and messaging security.

Source

Thrown at orderer/consensus/etcdraft/util.go:78

func MetadataHasDuplication(md *etcdraft.ConfigMetadata) error {
	if md == nil {
		return errors.New("nil metadata")
	}

	for _, consenter := range md.GetConsenters() {
		if consenter == nil {
			return errors.New("nil consenter in metadata")
		}
	}

	seen := make(map[string]struct{})
	for _, consenter := range md.GetConsenters() {
		serverKey := string(consenter.GetServerTlsCert())
		clientKey := string(consenter.GetClientTlsCert())
		_, duplicateServerCert := seen[serverKey]
		_, duplicateClientCert := seen[clientKey]
		if duplicateServerCert || duplicateClientCert {
			return errors.Errorf("duplicate consenter: server cert: %s, client cert: %s", serverKey, clientKey)
		}

		seen[serverKey] = struct{}{}
		seen[clientKey] = struct{}{}
	}
	return nil
}

// MetadataFromConfigValue reads and translates configuration updates from config value into raft metadata
// In case consensus type is changed to BFT the raft metadata will be nil
func MetadataFromConfigValue(configValue *common.ConfigValue) (*etcdraft.ConfigMetadata, *orderer.ConsensusType, error) {
	consensusTypeValue := &orderer.ConsensusType{}
	if err := proto.Unmarshal(configValue.GetValue(), consensusTypeValue); err != nil {
		return nil, nil, errors.Wrap(err, "failed to unmarshal consensusType config update")
	}

	if consensusTypeValue.GetType() != "etcdraft" {
		return nil, consensusTypeValue, nil

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Issue unique server and client TLS certificates for each consenter and regenerate the config update
  2. Search the metadata for the duplicated cert (the error prints the certs) and replace the offending entry
  3. Re-run VerifyConfigMetadata before submitting the corrected update

Example fix

// before
consenter2.ServerTlsCert = consenter1.ServerTlsCert // duplicate
// after
consenter2.ServerTlsCert = newServerCertFromTLS2
consenter2.ClientTlsCert = newClientCertFromTLS2
Defensive patterns

Strategy: validation

Validate before calling

func certsUnique(md *etcdraft.ConfigMetadata) error {
	seen := map[string]string{} // cert -> consenter
	for i, c := range md.GetConsenters() {
		for _, cert := range [][]byte{c.GetServerTlsCert(), c.GetClientTlsCert()} {
			key := string(cert)
			if owner, dup := seen[key]; dup {
				return fmt.Errorf("cert of consenter %d already used by %s", i, owner)
			}
			seen[key] = fmt.Sprintf("consenter-%d", i)
		}
	}
	return nil
}

Prevention

When it happens

Trigger: A channel config update adds or modifies a consenter reusing the server or client TLS cert PEM bytes of an existing consenter (or reusing the same cert within one consenter's server/client fields across nodes).

Common situations: Operators copying an existing orderer's TLS certs when onboarding a new node instead of issuing fresh ones; swapping certs for a replacement node but reusing the old node's certs; cert rotation scripts applying one cert to multiple orderers.

Related errors


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