hyperledger/fabric · error

nil consenter in metadata

Error message

nil consenter in metadata

What it means

While iterating over the consenters in ConfigMetadata to detect duplicates, MetadataHasDuplication finds a nil consenter entry and rejects the whole config update with this error. A nil consenter would have no TLS certificates and cannot participate in raft, so the metadata is invalid.

Source

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

func ConsentersToMap(consenters []*etcdraft.Consenter) ConsentersMap {
	set := map[string]struct{}{}
	for _, c := range consenters {
		set[string(c.GetClientTlsCert())] = struct{}{}
	}
	return set
}

// MetadataHasDuplication returns an error if the metadata has duplication of consenters.
// A duplication is defined by having a server or a client TLS certificate that is found
// in two different consenters, regardless of the type of certificate (client/server).
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
}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Regenerate the config update ensuring every consenter has host, port, and client/server TLS certs set
  2. Validate the metadata with VerifyConfigMetadata before submitting the update transaction
  3. Fix the generating code to never append nil/zero-value Consenter messages

Example fix

// before
metadata.Consenters = append(metadata.Consenters, &etcdraft.Consenter{})
// after
metadata.Consenters = append(metadata.Consenters, &etcdraft.Consenter{
	Host: host, Port: port,
	ServerTlsCert: serverCert, ClientTlsCert: clientCert,
})
Defensive patterns

Strategy: validation

Validate before calling

for i, c := range metadata.GetConsenters() {
	if c == nil {
		return fmt.Errorf("consenter %d is nil", i)
	}
	if c.GetHost() == "" || c.GetPort() == 0 || len(c.GetServerTlsCert()) == 0 || len(c.GetClientTlsCert()) == 0 {
		return fmt.Errorf("consenter %d incomplete", i)
	}
}

Type guard

func isValidConsenter(c *etcdraft.Consenter) bool {
	return c != nil && c.GetHost() != "" && c.GetPort() != 0 &&
		len(c.GetServerTlsCert()) > 0 && len(c.GetClientTlsCert()) > 0
}

Prevention

When it happens

Trigger: A channel config update whose etcdraft metadata's consenter list contains a nil entry — usually from programmatically appending an unset *etcdraft.Consenter or a partially built protobuf message.

Common situations: Custom channel-creation/update scripts appending an empty consenter element; SDK/config generators producing a repeated field with one uninitialized entry; hand-edited JSON config converted to protobuf.

Related errors


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