{"record":{"id":"0a29e48f9c7c33c2","repo":"hyperledger/fabric","slug":"config-update-contains-no-changes","errorCode":null,"errorMessage":"config update contains no changes","messagePattern":"config update contains no changes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/common/msgprocessor/maintenancefilter.go","lineNumber":180,"sourceCode":"\n\treturn nil\n}\n\n// ensureConsensusTypeChangeOnly checks that the only change is the Channel/Orderer group, and within that,\n// only to the ConsensusType value.\nfunc (mf *MaintenanceFilter) ensureConsensusTypeChangeOnly(configEnvelope *cb.ConfigEnvelope) error {\n\tconfigUpdateEnv, err := protoutil.EnvelopeToConfigUpdate(configEnvelope.LastUpdate)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"envelope to config update unmarshalling error\")\n\t}\n\n\tconfigUpdate, err := configtx.UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"config update unmarshalling error\")\n\t}\n\n\tif len(configUpdate.WriteSet.Groups) == 0 {\n\t\treturn errors.New(\"config update contains no changes\")\n\t}\n\n\tif len(configUpdate.WriteSet.Values) > 0 {\n\t\treturn errors.Errorf(\"config update contains changes to values in group %s\", channelconfig.ChannelGroupKey)\n\t}\n\n\tif len(configUpdate.WriteSet.Groups) > 1 {\n\t\treturn errors.New(\"config update contains changes to more than one group\")\n\t}\n\n\tif ordGroup, ok1 := configUpdate.WriteSet.Groups[channelconfig.OrdererGroupKey]; ok1 {\n\t\tif len(ordGroup.Groups) > 0 {\n\t\t\treturn errors.Errorf(\"config update contains changes to groups within the %s group\",\n\t\t\t\tchannelconfig.OrdererGroupKey)\n\t\t}\n\n\t\tif _, ok2 := ordGroup.Values[channelconfig.ConsensusTypeKey]; !ok2 {\n\t\t\treturn errors.Errorf(\"config update does not contain the %s value\", channelconfig.ConsensusTypeKey)","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/msgprocessor/maintenancefilter.go#L162-L198","documentation":"Returned when the parsed config update's WriteSet contains no group entries at all, meaning the update carries no changes that the maintenance filter can interpret as a consensus type migration. During migration, only a change to the Orderer group's ConsensusType value is permitted, so an empty write set is rejected.","triggerScenarios":"Submitting a config update during maintenance mode where the WriteSet.Groups map is empty — e.g. a no-op update, an update touching nothing, or a delta that failed to compute any group-level change.","commonSituations":"Computing a delta between two identical configs, or accidentally submitting the original config as the update so the resulting write set is empty.","solutions":["Recompute the config update delta with configtxlator compute-update between the correct current and desired config blocks","Confirm the update actually changes the Orderer group's ConsensusType value","If no change is intended, do not submit an update during the migration window"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if len(update.WriteSet.Groups) == 0 {\n    return errors.New(\"update write set has no group changes; recompute delta with configtxlator\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify the two config blocks fed to compute-update actually differ","Inspect the decoded update JSON for a non-empty Groups map before signing","Avoid submitting updates during maintenance mode unless they migrate the consensus type"],"tags":["hyperledger-fabric","orderer","config-update","migration"],"backgroundTag":"empty-config-update","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"}