{"record":{"id":"4dd9acf82125725a","repo":"hyperledger/fabric","slug":"ordererorg-config-does-not-allow-sub-groups","errorCode":null,"errorMessage":"OrdererOrg config does not allow sub-groups","messagePattern":"OrdererOrg config does not allow sub-groups","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/channelconfig/orderer.go","lineNumber":89,"sourceCode":"type OrdererOrgConfig struct {\n\t*OrganizationConfig\n\tprotos *OrdererOrgProtos\n\tname   string\n}\n\n// Endpoints returns the set of addresses this ordering org exposes as orderers\nfunc (oc *OrdererOrgConfig) Endpoints() []string {\n\tif oc.protos == nil || oc.protos.Endpoints == nil {\n\t\treturn nil\n\t}\n\n\treturn oc.protos.Endpoints.Addresses\n}\n\n// NewOrdererOrgConfig returns an orderer org config built from the given ConfigGroup.\nfunc NewOrdererOrgConfig(orgName string, orgGroup *cb.ConfigGroup, mspConfigHandler *MSPConfigHandler, channelCapabilities ChannelCapabilities) (*OrdererOrgConfig, error) {\n\tif len(orgGroup.Groups) > 0 {\n\t\treturn nil, fmt.Errorf(\"OrdererOrg config does not allow sub-groups\")\n\t}\n\n\tif !channelCapabilities.OrgSpecificOrdererEndpoints() {\n\t\tif _, ok := orgGroup.Values[EndpointsKey]; ok {\n\t\t\treturn nil, errors.Errorf(\"Orderer Org %s cannot contain endpoints value until V1_4_2+ capabilities have been enabled\", orgName)\n\t\t}\n\t}\n\n\tprotos := &OrdererOrgProtos{}\n\torgProtos := &OrganizationProtos{}\n\n\tif err := DeserializeProtoValuesFromGroup(orgGroup, protos, orgProtos); err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to deserialize values\")\n\t}\n\n\tooc := &OrdererOrgConfig{\n\t\tname:   orgName,\n\t\tprotos: protos,","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/common/channelconfig/orderer.go#L71-L107","documentation":"NewOrdererOrgConfig builds an orderer org config from a ConfigGroup. Orderer orgs must be leaf groups — they may not contain nested sub-groups. If orgGroup.Groups is non-empty, the construction fails with 'OrdererOrg config does not allow sub-groups'.","triggerScenarios":"Calling NewOrdererOrgConfig (via NewOrdererConfig during channel config construction) with an orderer org ConfigGroup that has nested Groups entries, e.g. misplaced orgs nested inside an orderer org group.","commonSituations":"configtx.yaml structured incorrectly so orgs or sub-groups end up under the Orderer group's org entries; programmatic config assembly that nests groups under an orderer org; copy-paste errors when editing config transactions.","solutions":["Flatten the config so orderer orgs contain only Values and Policies, no Groups","Move any nested groups to the correct level (Channel > Groups > Orderer > Groups > <OrgName>)","Re-run configtxgen with a corrected configtx.yaml profile to regenerate the config"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if len(orgGroup.Groups) > 0 {\n    return fmt.Errorf(\"orderer org group must not contain sub-groups; found %d\", len(orgGroup.Groups))\n}","typeGuard":"func isLeafGroup(g *cb.ConfigGroup) bool { return len(g.Groups) == 0 }","tryCatchPattern":"cfg, err := NewOrdererOrgConfig(orgName, orgGroup, mspHandler, caps)\nif err != nil && strings.Contains(err.Error(), \"does not allow sub-groups\") {\n    log.Errorf(\"restructure config: nested groups under orderer org %s are invalid\", orgName)\n    return err\n}","preventionTips":["Keep orderer org groups as leaves: only Values and Policies inside","Generate profiles with configtxgen instead of hand-assembling ConfigGroups","Validate group nesting with configtxlator proto_decode before submission"],"tags":["hyperledger-fabric","channel-config","orderer","config-structure"],"backgroundTag":"invalid-config-group-structure","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"}