{"record":{"id":"f63a5e1ac7795556","repo":"hyperledger/fabric","slug":"invalid-new-config-the-number-of-bft-consenters","errorCode":null,"errorMessage":"Invalid new config: the number of bft consenters: %d is not equal to the number of raft consenters: %d","messagePattern":"Invalid new config: the number of bft consenters: (.+?) is not equal to the number of raft consenters: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/common/msgprocessor/maintenancefilter.go","lineNumber":226,"sourceCode":"\n\treturn nil\n}\n\nfunc validateBFTConsenterMapping(currentOrdererConfig channelconfig.Orderer, nextOrdererConfig channelconfig.Orderer) error {\n\t// extract raft consenters from consensusTypeValue.metadata\n\traftMetadata := &etcdraft.ConfigMetadata{}\n\tproto.Unmarshal(currentOrdererConfig.ConsensusMetadata(), raftMetadata)\n\traftConsenters := raftMetadata.GetConsenters()\n\n\t// extract bft consenters\n\tbftConsenters := nextOrdererConfig.Consenters()\n\n\tif len(bftConsenters) == 0 {\n\t\treturn errors.Errorf(\"Invalid new config: bft consenters are missing\")\n\t}\n\n\tif len(raftConsenters) != len(bftConsenters) {\n\t\treturn errors.Errorf(\"Invalid new config: the number of bft consenters: %d is not equal to the number of raft consenters: %d\", len(bftConsenters), len(raftConsenters))\n\t}\n\n\tfor _, raftConsenter := range raftConsenters {\n\t\tflag := false\n\t\tfor _, bftConsenter := range bftConsenters {\n\t\t\tif raftConsenter.Port == bftConsenter.Port && raftConsenter.Host == bftConsenter.Host &&\n\t\t\t\tbytes.Equal(raftConsenter.ServerTlsCert, bftConsenter.ServerTlsCert) &&\n\t\t\t\tbytes.Equal(raftConsenter.ClientTlsCert, bftConsenter.ClientTlsCert) {\n\t\t\t\tflag = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !flag {\n\t\t\treturn errors.Errorf(\"No suitable BFT consenter for Raft consenter: %v\", raftConsenter)\n\t\t}\n\t}\n\n\treturn nil","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/msgprocessor/maintenancefilter.go#L208-L244","documentation":"For a Raft-to-BFT migration, the number of consenters in the new BFT config must exactly match the number of consenters in the current Raft metadata. A mismatch would add or drop ordering nodes mid-migration, which the migration procedure forbids.","triggerScenarios":"validateBFTConsenterMapping finds len(raftMetadata consenters) != len(nextOrdererConfig.Consenters()) — e.g. the proposed BFT metadata lists a different count of nodes than the current etcdraft metadata.","commonSituations":"Admins using migration as an opportunity to scale the ordering service up or down; forgetting that node additions/removals must be done separately (before or after migration, via channel reconfiguration).","solutions":["Adjust the BFT consenters list so it contains exactly the same number of entries (same nodes) as the current Raft consenters.","Perform any node addition/removal as a separate config step outside the consensus-type migration.","Diff both consenter lists before submitting to confirm equal counts."],"exampleFix":"// before\n// raft: 4 consenters, bft metadata: 3 consenters\n// after\n// bft Consenters array padded to the same 4 nodes as raft metadata","handlingStrategy":"validation","validationCode":"raftCount := len(currentRaftMetadata.GetConsenters())\nbftCount := len(newMetadata.GetConsenters())\nif raftCount != bftCount {\n    return fmt.Errorf(\"consenter count mismatch: raft=%d bft=%d\", raftCount, bftCount)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep node counts identical across the migration; scale the ordering service in separate steps.","Programmatically compare both consenter arrays before signing the update.","Document that node add/remove is prohibited during migration windows."],"tags":["hyperledger-fabric","raft","bft","consensus-migration"],"backgroundTag":"consenter-config-invalid","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"}