hyperledger/fabric · error

next config attempted to change ConsensusType.State to %s, b

Error message

next config attempted to change ConsensusType.State to %s, but capability is disabled

What it means

inspect rejects a config update that changes orderer ConsensusType.State while the ConsensusTypeMigration capability is disabled in the channel. State may only leave STATE_NORMAL when migration capability is enabled, so any proposed STATE_MAINTENANCE transition fails with the proposed state name in the message.

Source

Thrown at orderer/common/msgprocessor/maintenancefilter.go:96

// transition rules of consensus-type migration using maintenance-mode.
func (mf *MaintenanceFilter) inspect(configEnvelope *cb.ConfigEnvelope, ordererConfig channelconfig.Orderer) error {
	if configEnvelope.LastUpdate == nil {
		return errors.Errorf("updated config does not include a config update")
	}

	bundle, err := channelconfig.NewBundle(mf.support.ChannelID(), configEnvelope.Config, mf.bccsp)
	if err != nil {
		return errors.Wrap(err, "failed to parse config")
	}

	nextOrdererConfig, ok := bundle.OrdererConfig()
	if !ok {
		return errors.New("next config is missing orderer group")
	}

	if !ordererConfig.Capabilities().ConsensusTypeMigration() {
		if nextState := nextOrdererConfig.ConsensusState(); nextState != orderer.ConsensusType_STATE_NORMAL {
			return errors.Errorf("next config attempted to change ConsensusType.State to %s, but capability is disabled", nextState)
		}
		if ordererConfig.ConsensusType() != nextOrdererConfig.ConsensusType() {
			return errors.Errorf("next config attempted to change ConsensusType.Type from %s to %s, but capability is disabled",
				ordererConfig.ConsensusType(), nextOrdererConfig.ConsensusType())
		}
		return nil
	}

	// Entry to- and exit from- maintenance-mode should not be accompanied by any other change.
	if ordererConfig.ConsensusState() != nextOrdererConfig.ConsensusState() {
		if err1Change := mf.ensureConsensusTypeChangeOnly(configEnvelope); err1Change != nil {
			return err1Change
		}
		if ordererConfig.ConsensusType() != nextOrdererConfig.ConsensusType() {
			return errors.Errorf("attempted to change ConsensusType.Type from %s to %s, but ConsensusType.State is changing from %s to %s",
				ordererConfig.ConsensusType(), nextOrdererConfig.ConsensusType(), ordererConfig.ConsensusState(), nextOrdererConfig.ConsensusState())
		}
		if !bytes.Equal(nextOrdererConfig.ConsensusMetadata(), ordererConfig.ConsensusMetadata()) {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. First enable the ConsensusTypeMigration capability (channel + orderer capabilities, e.g. V2_0 or per the migration guide) in a separate config update
  2. Verify with configtxlator that capabilities.ConsensusTypeMigration() is true on the current channel
  3. Retry the maintenance-mode entry after the capability update has been committed
Defensive patterns

Strategy: validation

Validate before calling

oc, _ := support.OrdererConfig()
if !oc.Capabilities().ConsensusTypeMigration() {
	return errors.New("enable ConsensusTypeMigration capability before entering maintenance mode")
}

Prevention

When it happens

Trigger: Submitting a config update setting orderer group's ConsensusType.State to STATE_MAINTENANCE (or any non-NORMAL value) on a channel whose capabilities do not include ConsensusTypeMigration (pre-V2_0 capability or V3 channel without it).

Common situations: Operator begins raft-to-BFT migration without first enabling the migration capability; channel created with older capabilities; capability update not committed on the orderer group.

Related errors


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