hyperledger/fabric · error

OrdererOrg config does not allow sub-groups

Error message

OrdererOrg config does not allow sub-groups

What it means

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'.

Solutions

  1. Flatten the config so orderer orgs contain only Values and Policies, no Groups
  2. Move any nested groups to the correct level (Channel > Groups > Orderer > Groups > <OrgName>)
  3. Re-run configtxgen with a corrected configtx.yaml profile to regenerate the config
Defensive patterns

Strategy: validation

Validate before calling

if len(orgGroup.Groups) > 0 {
    return fmt.Errorf("orderer org group must not contain sub-groups; found %d", len(orgGroup.Groups))
}

Type guard

func isLeafGroup(g *cb.ConfigGroup) bool { return len(g.Groups) == 0 }

Try / catch

cfg, err := NewOrdererOrgConfig(orgName, orgGroup, mspHandler, caps)
if err != nil && strings.Contains(err.Error(), "does not allow sub-groups") {
    log.Errorf("restructure config: nested groups under orderer org %s are invalid", orgName)
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at common/channelconfig/orderer.go:89

type OrdererOrgConfig struct {
	*OrganizationConfig
	protos *OrdererOrgProtos
	name   string
}

// Endpoints returns the set of addresses this ordering org exposes as orderers
func (oc *OrdererOrgConfig) Endpoints() []string {
	if oc.protos == nil || oc.protos.Endpoints == nil {
		return nil
	}

	return oc.protos.Endpoints.Addresses
}

// NewOrdererOrgConfig returns an orderer org config built from the given ConfigGroup.
func NewOrdererOrgConfig(orgName string, orgGroup *cb.ConfigGroup, mspConfigHandler *MSPConfigHandler, channelCapabilities ChannelCapabilities) (*OrdererOrgConfig, error) {
	if len(orgGroup.Groups) > 0 {
		return nil, fmt.Errorf("OrdererOrg config does not allow sub-groups")
	}

	if !channelCapabilities.OrgSpecificOrdererEndpoints() {
		if _, ok := orgGroup.Values[EndpointsKey]; ok {
			return nil, errors.Errorf("Orderer Org %s cannot contain endpoints value until V1_4_2+ capabilities have been enabled", orgName)
		}
	}

	protos := &OrdererOrgProtos{}
	orgProtos := &OrganizationProtos{}

	if err := DeserializeProtoValuesFromGroup(orgGroup, protos, orgProtos); err != nil {
		return nil, errors.Wrap(err, "failed to deserialize values")
	}

	ooc := &OrdererOrgConfig{
		name:   orgName,
		protos: protos,

View on GitHub (pinned to 2736b63f8f)