hyperledger/fabric · error

attempted to change consensus type from %s to %s, but next c

Error message

attempted to change consensus type from %s to %s, but next config ConsensusType.State is not in maintenance mode

What it means

Thrown by the maintenance config filter (inspect, called from Apply) when a config update attempts to change the orderer consensus type while the resulting (next) config's ConsensusType.State is STATE_NORMAL. Consensus-type migration in Hyperledger Fabric is only allowed while the channel is in maintenance mode, for both the current and the next config. The orderer rejects the update so a partial or invalid migration cannot enter the channel config.

Source

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

		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()) {
			return errors.Errorf("attempted to change ConsensusType.Metadata, but ConsensusType.State is changing from %s to %s",
				ordererConfig.ConsensusState(), nextOrdererConfig.ConsensusState())
		}
	}

	// ConsensusType.Type can only change in maintenance-mode, and only within the set of permitted types.
	// Note: only etcdraft to BFT transitions are supported.
	if ordererConfig.ConsensusType() != nextOrdererConfig.ConsensusType() {
		if ordererConfig.ConsensusState() == orderer.ConsensusType_STATE_NORMAL {
			return errors.Errorf("attempted to change consensus type from %s to %s, but current config ConsensusType.State is not in maintenance mode",
				ordererConfig.ConsensusType(), nextOrdererConfig.ConsensusType())
		}
		if nextOrdererConfig.ConsensusState() == orderer.ConsensusType_STATE_NORMAL {
			return errors.Errorf("attempted to change consensus type from %s to %s, but next config ConsensusType.State is not in maintenance mode",
				ordererConfig.ConsensusType(), nextOrdererConfig.ConsensusType())
		}

		if !mf.permittedTargetConsensusTypes[nextOrdererConfig.ConsensusType()] {
			return errors.Errorf("attempted to change consensus type from %s to %s, transition not supported",
				ordererConfig.ConsensusType(), nextOrdererConfig.ConsensusType())
		}

		if nextOrdererConfig.ConsensusType() == "BFT" {
			updatedMetadata := &smartbft.Options{}
			if err := proto.Unmarshal(nextOrdererConfig.ConsensusMetadata(), updatedMetadata); err != nil {
				return errors.Wrap(err, "failed to unmarshal BFT metadata configuration")
			}

			_, err := util.ConfigFromMetadataOptions(1, updatedMetadata)
			if err != nil {
				return errors.New("invalid BFT metadata configuration")
			}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Set ConsensusType.State to STATE_MAINTENANCE in the next config whenever ConsensusType is being changed
  2. Split the migration into separate updates: (1) enter maintenance mode, (2) change consensus type while in maintenance, (3) switch state back to NORMAL after all orderers restarted
  3. Regenerate the config update with configtxlator so the State field is preserved as STATE_MAINTENANCE

Example fix

// before: changing type and leaving maintenance in one update
"ConsensusType": { "State": "STATE_NORMAL", "Type": "BFT" }
// after: change type while still in maintenance
"ConsensusType": { "State": "STATE_MAINTENANCE", "Type": "BFT" }
// then a separate update switches State back to STATE_NORMAL
Defensive patterns

Strategy: validation

Validate before calling

// Go: validate next config before submitting
if next.Orderer.ConsensusType != cur.Orderer.ConsensusType &&
   next.Orderer.ConsensusState != orderer.ConsensusType_STATE_MAINTENANCE {
    return errors.New("consensus type change requires next config ConsensusType.State = STATE_MAINTENANCE")
}

Type guard

func inMaintenance(s orderer.ConsensusType_State) bool { return s == orderer.ConsensusType_STATE_MAINTENANCE }

Prevention

When it happens

Trigger: Submitting a channel config update that changes consensus type (e.g. etcdraft to BFT or back) where the previous config was in maintenance mode (STATE_MAINTENANCE) but the proposed next config sets ConsensusType.State back to STATE_NORMAL in the same update as the type change.

Common situations: Admins following the consensus migration procedure forget the two-phase requirement: first switch state to maintenance, then change the type while staying in maintenance. Combining 'change type' and 'switch back to normal' into one update produces this error. Also common when generating the config update with automated tooling that resets State to NORMAL.

Related errors


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