{"record":{"id":"3db7bac6c685f88c","repo":"hyperledger/fabric","slug":"nil-metadata","errorCode":null,"errorMessage":"nil metadata","messagePattern":"nil metadata","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/etcdraft/util.go","lineNumber":62,"sourceCode":"\t_, exists := c[string(consenter.GetClientTlsCert())]\n\treturn exists\n}\n\n// ConsentersToMap maps consenters into set where key is client TLS certificate\nfunc 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","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/etcdraft/util.go#L44-L80","documentation":"MetadataHasDuplication validates the etcdraft ConfigMetadata carried in a channel config update. It returns this error immediately when the metadata pointer itself is nil, since there are no consenters to validate at all. Callers use it via VerifyConfigMetadata to reject malformed config updates before they are committed.","triggerScenarios":"VerifyConfigMetadata (or MetadataFromConfigValue returning a nil metadata for a type change) hands a nil *etcdraft.ConfigMetadata into MetadataHasDuplication — e.g. a config update whose consensusType metadata field is empty.","commonSituations":"Channel update transaction built without the metadata field populated; a config tool marshaling an empty ConfigMetadata; switching consensus type where metadata is legitimately nil but validators are invoked unconditionally.","solutions":["Ensure the config-update tooling marshals a valid ConfigMetadata with the full consenter list into consensusType.metadata","Re-generate the channel config update with the correct protobuf field set (metadata containing consenters)","If the error comes from your own caller, check md == nil before calling MetadataHasDuplication"],"exampleFix":"// before\nif err := MetadataHasDuplication(md); err != nil { ... }\n// after\nif md == nil {\n\treturn errors.New(\"raft metadata missing from config update\")\n}\nif err := MetadataHasDuplication(md); err != nil { ... }","handlingStrategy":"validation","validationCode":"// before invoking validation on config update\ncv, _ := MetadataFromConfigValue(configValue)\nif cv == nil {\n\treturn errors.New(\"config update carries no raft metadata\")\n}\nif err := VerifyConfigMetadata(cv); err != nil {\n\treturn fmt.Errorf(\"invalid raft metadata: %w\", err)\n}","typeGuard":"func hasRaftMetadata(md *etcdraft.ConfigMetadata) bool {\n\treturn md != nil\n}","tryCatchPattern":null,"preventionTips":["Always populate consensusType.metadata when submitting etcdraft config updates","Validate updates with configtxlator before submitting","Check for nil metadata in tooling that switches consensus type","Write integration tests that round-trip the config update protobuf"],"tags":["raft","config","validation","metadata"],"backgroundTag":"nil-metadata-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"}