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, nilView on GitHub (pinned to 2736b63f8f)
Solutions
- Issue unique server and client TLS certificates for each consenter and regenerate the config update
- Search the metadata for the duplicated cert (the error prints the certs) and replace the offending entry
- 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
- Issue unique TLS certs per orderer node — never copy between nodes
- Track cert assignments in an inventory when rotating certificates
- Run MetadataHasDuplication locally on the proposed metadata before submitting
- When replacing a node, revoke and replace the old node's certs rather than reusing them
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
- consenter %s:%d has invalid certificates
- without a system channel, a follower should have been create
- nil consenter in metadata
- unable to load key at '%s'
- unable to load cert at '%s'
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/c0381afe90291de2.
Report an issue: GitHub.