hyperledger/fabric · error

orderer type BFT must be used with V3_0 channel capability:

Error message

orderer type BFT must be used with V3_0 channel capability: %v

What it means

configtxgen refuses to build a channel/orderer config group when the orderer type is BFT (SmartBFT) but the channel definition does not enable the V3_0 capability. BFT consensus depends on V3_0 channel semantics, so the encoder fails fast at genesis-block/config creation time. The error message includes the full channelCapabilities map so you can see what is enabled.

Source

Thrown at internal/configtxgen/encoder/encoder.go:187

	}

	if conf.Consortiums != nil {
		channelGroup.Groups[channelconfig.ConsortiumsGroupKey], err = NewConsortiumsGroup(conf.Consortiums)
		if err != nil {
			return nil, errors.Wrap(err, "could not create consortiums group")
		}
	}

	channelGroup.ModPolicy = channelconfig.AdminsPolicyKey
	return channelGroup, nil
}

// NewOrdererGroup returns the orderer component of the channel configuration.  It defines parameters of the ordering service
// about how large blocks should be, how frequently they should be emitted, etc. as well as the organizations of the ordering network.
// It sets the mod_policy of all elements to "Admins".  This group is always present in any channel configuration.
func NewOrdererGroup(conf *genesisconfig.Orderer, channelCapabilities map[string]bool) (*cb.ConfigGroup, error) {
	if conf.OrdererType == "BFT" && !channelCapabilities["V3_0"] {
		return nil, errors.Errorf("orderer type BFT must be used with V3_0 channel capability: %v", channelCapabilities)
	}
	if len(conf.Addresses) > 0 && channelCapabilities["V3_0"] {
		return nil, errors.Errorf("global orderer endpoints exist, but can not be used with V3_0 capability: %v", conf.Addresses)
	}

	ordererGroup := protoutil.NewConfigGroup()
	if err := AddOrdererPolicies(ordererGroup, conf.Policies, channelconfig.AdminsPolicyKey); err != nil {
		return nil, errors.Wrapf(err, "error adding policies to orderer group")
	}
	addValue(ordererGroup, channelconfig.BatchSizeValue(
		conf.BatchSize.MaxMessageCount,
		conf.BatchSize.AbsoluteMaxBytes,
		conf.BatchSize.PreferredMaxBytes,
	), channelconfig.AdminsPolicyKey)
	addValue(ordererGroup, channelconfig.BatchTimeoutValue(conf.BatchTimeout.String()), channelconfig.AdminsPolicyKey)
	addValue(ordererGroup, channelconfig.ChannelRestrictionsValue(conf.MaxChannels), channelconfig.AdminsPolicyKey)

	if len(conf.Capabilities) > 0 {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Add V3_0 to Channel.Capabilities in your configtx.yaml profile: Capabilities: Channel: V3_0: {}
  2. Confirm Orderer.OrdererType is intentionally BFT; if not, revert it to etcdraft (or solo) so V3_0 is not required
  3. Regenerate the genesis block/channel tx after the capability change: configtxgen -profile <profile> -outputBlock genesis.block

Example fix

# before
Capabilities:
    Channel: &ChannelCapabilities
        V2_0: true
Orderer:
    OrdererType: BFT
# after
Capabilities:
    Channel: &ChannelCapabilities
        V2_0: true
        V3_0: true
Orderer:
    OrdererType: BFT
Defensive patterns

Strategy: validation

Validate before calling

// Go: validate before calling encoder.NewOrdererGroup
func validateBFTCapability(conf *genesisconfig.Orderer, caps map[string]bool) error {
    if conf.OrdererType == "BFT" && !caps["V3_0"] {
        return fmt.Errorf("BFT orderer requires V3_0 channel capability; got %v", caps)
    }
    return nil
}

Type guard

func isBFT(conf *genesisconfig.Orderer) bool { return conf.OrdererType == "BFT" }
// usage: if isBFT(conf) && !channelCapabilities["V3_0"] { /* fix caps first */ }

Try / catch

group, err := encoder.NewOrdererGroup(conf, caps)
if err != nil {
    if strings.Contains(err.Error(), "must be used with V3_0") {
        return fmt.Errorf("profile misconfiguration: enable V3_0 channel capability in configtx.yaml: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling encoder.NewOrdererGroup (directly or via NewChannelNewConfig/NewChannelGroup from configtxgen OutputBlock/CreateChannelTx) with conf.OrdererType == "BFT" in configtx.yaml while channel Capabilities does not include the "V3_0" key.

Common situations: Developers switch Orderer.OrdererType from etcdraft to BFT in an existing configtx.yaml that still carries an older capabilities set (e.g. V2_0), or copy a BFT sample profile without adding V3_0 to Channel.Capabilities.

Related errors


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