{"record":{"id":"1bacf5903bd937e9","repo":"hyperledger/fabric","slug":"attempted-to-change-consensustype-type-from-s-to","errorCode":null,"errorMessage":"attempted to change ConsensusType.Type from %s to %s, but ConsensusType.State is changing from %s to %s","messagePattern":"attempted to change ConsensusType\\.Type from (.+?) to (.+?), but ConsensusType\\.State is changing from (.+?) to (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/common/msgprocessor/maintenancefilter.go","lineNumber":111,"sourceCode":"\n\tif !ordererConfig.Capabilities().ConsensusTypeMigration() {\n\t\tif nextState := nextOrdererConfig.ConsensusState(); nextState != orderer.ConsensusType_STATE_NORMAL {\n\t\t\treturn errors.Errorf(\"next config attempted to change ConsensusType.State to %s, but capability is disabled\", nextState)\n\t\t}\n\t\tif ordererConfig.ConsensusType() != nextOrdererConfig.ConsensusType() {\n\t\t\treturn errors.Errorf(\"next config attempted to change ConsensusType.Type from %s to %s, but capability is disabled\",\n\t\t\t\tordererConfig.ConsensusType(), nextOrdererConfig.ConsensusType())\n\t\t}\n\t\treturn nil\n\t}\n\n\t// Entry to- and exit from- maintenance-mode should not be accompanied by any other change.\n\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())","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/msgprocessor/maintenancefilter.go#L93-L129","documentation":"inspect rejects a config update that changes both ConsensusType.Type and ConsensusType.State at once. Maintenance-mode transitions require the state change to be accompanied only by the ConsensusType value change (checked by ensureConsensusTypeChangeOnly) — changing the type in the same update as the state is forbidden, so entry/exit from maintenance and the type swap must be separate config updates.","triggerScenarios":"A single config update whose Orderer-group write set changes both ConsensusType.Type and ConsensusType.State (e.g. etcdraft/NORMAL to BFT/MAINTENANCE in one shot).","commonSituations":"Operator tries to shortcut the raft-to-BFT migration by combining steps; scripted update generated from an incorrect diff of current vs final config.","solutions":["Split into sequential updates: (1) enter STATE_MAINTENANCE with type unchanged, (2) change ConsensusType.Type while staying in maintenance, (3) exit to STATE_NORMAL","When entering/exiting maintenance, ensure the type field in the update equals the current type","Generate each step's update by diffing configtxlator output of consecutive states"],"exampleFix":"// before: one update changing both\n\"state\": \"STATE_MAINTENANCE\", \"type\": \"BFT\"  // from etcdraft/NORMAL\n// after: two updates\n// step 1: {\"state\": \"STATE_MAINTENANCE\", \"type\": \"etcdraft\"}\n// step 2: {\"state\": \"STATE_MAINTENANCE\", \"type\": \"BFT\"}","handlingStrategy":"validation","validationCode":"stateChanging := cur.ConsensusState() != next.ConsensusState()\ntypeChanging := cur.ConsensusType() != next.ConsensusType()\nif stateChanging && typeChanging {\n\treturn errors.New(\"split into separate updates: state transition and type change cannot be combined\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["One dimension (state OR type) per config update","Generate each step's update by diffing consecutive configtxlator outputs","Follow the official raft-to-BFT migration checklist"],"tags":["ordering","consensus-migration","maintenance-mode","config-update"],"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"}