{"record":{"id":"78755ac6d81c6fe0","repo":"hyperledger/fabric","slug":"nil-consenter-in-metadata","errorCode":null,"errorMessage":"nil consenter in metadata","messagePattern":"nil consenter in metadata","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/etcdraft/util.go","lineNumber":67,"sourceCode":"func ConsentersToMap(consenters []*etcdraft.Consenter) ConsentersMap {\n\tset := map[string]struct{}{}\n\tfor _, c := range consenters {\n\t\tset[string(c.GetClientTlsCert())] = struct{}{}\n\t}\n\treturn set\n}\n\n// MetadataHasDuplication returns an error if the metadata has duplication of consenters.\n// A duplication is defined by having a server or a client TLS certificate that is found\n// in two different consenters, regardless of the type of certificate (client/server).\nfunc MetadataHasDuplication(md *etcdraft.ConfigMetadata) error {\n\tif md == nil {\n\t\treturn errors.New(\"nil metadata\")\n\t}\n\n\tfor _, consenter := range md.GetConsenters() {\n\t\tif consenter == nil {\n\t\t\treturn errors.New(\"nil consenter in metadata\")\n\t\t}\n\t}\n\n\tseen := make(map[string]struct{})\n\tfor _, consenter := range md.GetConsenters() {\n\t\tserverKey := string(consenter.GetServerTlsCert())\n\t\tclientKey := string(consenter.GetClientTlsCert())\n\t\t_, duplicateServerCert := seen[serverKey]\n\t\t_, duplicateClientCert := seen[clientKey]\n\t\tif duplicateServerCert || duplicateClientCert {\n\t\t\treturn errors.Errorf(\"duplicate consenter: server cert: %s, client cert: %s\", serverKey, clientKey)\n\t\t}\n\n\t\tseen[serverKey] = struct{}{}\n\t\tseen[clientKey] = struct{}{}\n\t}\n\treturn nil\n}","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/etcdraft/util.go#L49-L85","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Regenerate the config update ensuring every consenter has host, port, and client/server TLS certs set","Validate the metadata with VerifyConfigMetadata before submitting the update transaction","Fix the generating code to never append nil/zero-value Consenter messages"],"exampleFix":"// before\nmetadata.Consenters = append(metadata.Consenters, &etcdraft.Consenter{})\n// after\nmetadata.Consenters = append(metadata.Consenters, &etcdraft.Consenter{\n\tHost: host, Port: port,\n\tServerTlsCert: serverCert, ClientTlsCert: clientCert,\n})","handlingStrategy":"validation","validationCode":"for i, c := range metadata.GetConsenters() {\n\tif c == nil {\n\t\treturn fmt.Errorf(\"consenter %d is nil\", i)\n\t}\n\tif c.GetHost() == \"\" || c.GetPort() == 0 || len(c.GetServerTlsCert()) == 0 || len(c.GetClientTlsCert()) == 0 {\n\t\treturn fmt.Errorf(\"consenter %d incomplete\", i)\n\t}\n}","typeGuard":"func isValidConsenter(c *etcdraft.Consenter) bool {\n\treturn c != nil && c.GetHost() != \"\" && c.GetPort() != 0 &&\n\t\tlen(c.GetServerTlsCert()) > 0 && len(c.GetClientTlsCert()) > 0\n}","tryCatchPattern":null,"preventionTips":["Never append zero-value Consenter structs in config generators","Validate metadata with VerifyConfigMetadata before submitting updates","Round-trip marshal/unmarshal the metadata in tests","Review generated channel-update JSON for empty consenter entries"],"tags":["raft","config","validation","consenter"],"backgroundTag":"nil-consenter-config","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}