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
- Set ConsensusType.State to STATE_MAINTENANCE in the next config whenever ConsensusType is being changed
- 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
- 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
- Follow the documented three-phase migration: enter maintenance, change type, return to normal
- Never combine the type change and the state back-to-normal switch in one update
- Diff the generated update against the current config with configtxlator before submitting
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
- attempted to change consensus type from %s to %s, transition
- failed to unmarshal BFT metadata configuration
- envelope to config update unmarshalling error
- config update unmarshalling error
- config update contains no changes
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/0438378be3201936.
Report an issue: GitHub.