{"record":{"id":"d087f6c546e991e2","repo":"hyperledger/fabric","slug":"config-update-unmarshalling-error","errorCode":null,"errorMessage":"config update unmarshalling error","messagePattern":"config update unmarshalling error","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/common/msgprocessor/maintenancefilter.go","lineNumber":176,"sourceCode":"\tif nextOrdererConfig.ConsensusState() != ordererConfig.ConsensusState() {\n\t\tlogger.Infof(\"[channel: %s] maintenance mode: ConsensusType.State about to change from %s to %s\",\n\t\t\tmf.support.ChannelID(), ordererConfig.ConsensusState(), nextOrdererConfig.ConsensusState())\n\t}\n\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)","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/msgprocessor/maintenancefilter.go#L158-L194","documentation":"Wraps a failure to unmarshal ConfigUpdateEnv.ConfigUpdate into a cb.ConfigUpdate via configtx.UnmarshalConfigUpdate inside ensureConsensusTypeChangeOnly. The ConfigUpdateEnvelope carries a ConfigUpdate field that is not a valid serialized ConfigUpdate message.","triggerScenarios":"A config update envelope during migration whose ConfigUpdate bytes are corrupt, empty, or were re-encoded incorrectly after editing (e.g. wrong field populated, config update built by hand).","commonSituations":"Hand-assembling the update JSON and encoding the wrong section (encoding the whole config instead of the update), or truncating base64 during copy/paste between configtxlator steps.","solutions":["Regenerate the ConfigUpdate with configtxlator compute-update from the current and desired config blocks","Verify the ConfigUpdate field decodes cleanly before wrapping it in the envelope","Do not hand-edit the update JSON; edit only the decoded config and recompute the delta"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Go: sanity-check the ConfigUpdate bytes before wrapping\ncu := &cb.ConfigUpdate{}\nif err := proto.Unmarshal(cfgUpdateEnv.ConfigUpdate, cu); err != nil {\n    return fmt.Errorf(\"ConfigUpdate bytes invalid: %w\", err)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use configtxlator compute-update rather than hand-building the ConfigUpdate","Round-trip decode/encode the update JSON to catch encoding mistakes","Keep base64 payloads intact when copying between tools"],"tags":["hyperledger-fabric","orderer","configtxlator","config-update"],"backgroundTag":"protobuf-unmarshal-failed","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"}