{"record":{"id":"a73866b7e8885cd6","repo":"hyperledger/fabric","slug":"attempted-to-change-consensus-type-from-s-to-s-a73866","errorCode":null,"errorMessage":"attempted to change consensus type from %s to %s, but current config ConsensusType.State is not in maintenance mode","messagePattern":"attempted to change consensus type from (.+?) to (.+?), but current config ConsensusType\\.State is not in maintenance mode","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/common/msgprocessor/maintenancefilter.go","lineNumber":124,"sourceCode":"\tif ordererConfig.ConsensusState() != nextOrdererConfig.ConsensusState() {\n\t\tif err1Change := mf.ensureConsensusTypeChangeOnly(configEnvelope); err1Change != nil {\n\t\t\treturn err1Change\n\t\t}\n\t\tif ordererConfig.ConsensusType() != nextOrdererConfig.ConsensusType() {\n\t\t\treturn errors.Errorf(\"attempted to change ConsensusType.Type from %s to %s, but ConsensusType.State is changing from %s to %s\",\n\t\t\t\tordererConfig.ConsensusType(), nextOrdererConfig.ConsensusType(), ordererConfig.ConsensusState(), nextOrdererConfig.ConsensusState())\n\t\t}\n\t\tif !bytes.Equal(nextOrdererConfig.ConsensusMetadata(), ordererConfig.ConsensusMetadata()) {\n\t\t\treturn errors.Errorf(\"attempted to change ConsensusType.Metadata, but ConsensusType.State is changing from %s to %s\",\n\t\t\t\tordererConfig.ConsensusState(), nextOrdererConfig.ConsensusState())\n\t\t}\n\t}\n\n\t// ConsensusType.Type can only change in maintenance-mode, and only within the set of permitted types.\n\t// Note: only etcdraft to BFT transitions are supported.\n\tif ordererConfig.ConsensusType() != nextOrdererConfig.ConsensusType() {\n\t\tif ordererConfig.ConsensusState() == orderer.ConsensusType_STATE_NORMAL {\n\t\t\treturn errors.Errorf(\"attempted to change consensus type from %s to %s, but current config ConsensusType.State is not in maintenance mode\",\n\t\t\t\tordererConfig.ConsensusType(), nextOrdererConfig.ConsensusType())\n\t\t}\n\t\tif nextOrdererConfig.ConsensusState() == orderer.ConsensusType_STATE_NORMAL {\n\t\t\treturn errors.Errorf(\"attempted to change consensus type from %s to %s, but next config ConsensusType.State is not in maintenance mode\",\n\t\t\t\tordererConfig.ConsensusType(), nextOrdererConfig.ConsensusType())\n\t\t}\n\n\t\tif !mf.permittedTargetConsensusTypes[nextOrdererConfig.ConsensusType()] {\n\t\t\treturn errors.Errorf(\"attempted to change consensus type from %s to %s, transition not supported\",\n\t\t\t\tordererConfig.ConsensusType(), nextOrdererConfig.ConsensusType())\n\t\t}\n\n\t\tif nextOrdererConfig.ConsensusType() == \"BFT\" {\n\t\t\tupdatedMetadata := &smartbft.Options{}\n\t\t\tif err := proto.Unmarshal(nextOrdererConfig.ConsensusMetadata(), updatedMetadata); err != nil {\n\t\t\t\treturn errors.Wrap(err, \"failed to unmarshal BFT metadata configuration\")\n\t\t\t}\n","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/msgprocessor/maintenancefilter.go#L106-L142","documentation":"inspect rejects a change of ConsensusType.Type when the current config's ConsensusType.State is STATE_NORMAL. The consensus type may only be changed while the channel is in maintenance mode, so the update is rejected until the channel has first entered STATE_MAINTENANCE.","triggerScenarios":"Submitting a config update changing the orderer consensus type (e.g. etcdraft to BFT) while current ConsensusType.State is STATE_NORMAL.","commonSituations":"Skipping the maintenance-mode entry step of the raft-to-BFT migration procedure; operator assumes type can be changed directly; stale channel state assumption after another admin reverted to NORMAL.","solutions":["First submit a config update setting ConsensusType.State to STATE_MAINTENANCE (type unchanged)","Then change ConsensusType.Type to a permitted target (BFT) while in maintenance","Finally exit maintenance with a third update setting State to STATE_NORMAL","Check current state with `osnadmin channel list`/configtxlator before attempting the type change"],"exampleFix":"// before: type change attempted from NORMAL\n{\"type\": \"BFT\", \"state\": \"STATE_NORMAL\"}\n// after: enter maintenance first\n// update 1: {\"type\": \"etcdraft\", \"state\": \"STATE_MAINTENANCE\"}\n// update 2: {\"type\": \"BFT\", \"state\": \"STATE_MAINTENANCE\"}","handlingStrategy":"validation","validationCode":"cur, _ := support.OrdererConfig()\nif cur.ConsensusType() != next.ConsensusType() && cur.ConsensusState() == orderer.ConsensusType_STATE_NORMAL {\n\treturn errors.New(\"enter STATE_MAINTENANCE before changing consensus type\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check current consensus state before every type-change submission","Always execute the maintenance entry step first","Use configtxlator to read the live ConsensusType value to confirm state","Automate the 4-step migration sequence instead of ad-hoc updates"],"tags":["ordering","consensus-migration","maintenance-mode","consensus-type"],"backgroundTag":"consensus-type-migration-rule-violation","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}