{"record":{"id":"21a843f3603761d9","repo":"hyperledger/fabric","slug":"failed-to-unmarshal-consensustype-config-update","errorCode":null,"errorMessage":"failed to unmarshal consensusType config update","messagePattern":"failed to unmarshal consensusType config update","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/etcdraft/util.go","lineNumber":92,"sourceCode":"\t\tclientKey := string(consenter.GetClientTlsCert())\n\t\t_, duplicateServerCert := seen[serverKey]\n\t\t_, duplicateClientCert := seen[clientKey]\n\t\tif duplicateServerCert || duplicateClientCert {\n\t\t\treturn errors.Errorf(\"duplicate consenter: server cert: %s, client cert: %s\", serverKey, clientKey)\n\t\t}\n\n\t\tseen[serverKey] = struct{}{}\n\t\tseen[clientKey] = struct{}{}\n\t}\n\treturn nil\n}\n\n// MetadataFromConfigValue reads and translates configuration updates from config value into raft metadata\n// In case consensus type is changed to BFT the raft metadata will be nil\nfunc MetadataFromConfigValue(configValue *common.ConfigValue) (*etcdraft.ConfigMetadata, *orderer.ConsensusType, error) {\n\tconsensusTypeValue := &orderer.ConsensusType{}\n\tif err := proto.Unmarshal(configValue.GetValue(), consensusTypeValue); err != nil {\n\t\treturn nil, nil, errors.Wrap(err, \"failed to unmarshal consensusType config update\")\n\t}\n\n\tif consensusTypeValue.GetType() != \"etcdraft\" {\n\t\treturn nil, consensusTypeValue, nil\n\t}\n\n\tupdatedMetadata := &etcdraft.ConfigMetadata{}\n\tif err := proto.Unmarshal(consensusTypeValue.GetMetadata(), updatedMetadata); err != nil {\n\t\treturn nil, nil, errors.Wrap(err, \"failed to unmarshal updated (new) etcdraft metadata configuration\")\n\t}\n\n\treturn updatedMetadata, consensusTypeValue, nil\n}\n\n// MetadataFromConfigUpdate extracts consensus metadata from config update\nfunc MetadataFromConfigUpdate(update *common.ConfigUpdate) (*etcdraft.ConfigMetadata, *orderer.ConsensusType, error) {\n\tvar baseVersion uint64\n\tif update.GetReadSet() != nil && update.ReadSet.Groups != nil {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/etcdraft/util.go#L74-L110","documentation":"MetadataFromConfigValue unmarshals the consensusType configuration value bytes from a channel config update into an orderer.ConsensusType protobuf. If proto.Unmarshal fails, the bytes are not a valid ConsensusType message, and the error is wrapped with this message. Callers such as MetadataFromConfigUpdate then abort processing the config update.","triggerScenarios":"A channel config transaction carries a consensusType value whose bytes are corrupted, truncated, or encoded by an incompatible protobuf version/schema, causing proto.Unmarshal(configValue.GetValue(), consensusTypeValue) to fail.","commonSituations":"Hand-crafted or SDK-generated config updates with malformed value payloads; version skew between config-generation tooling and orderer protobuf definitions; binary corruption in stored config blocks being replayed.","solutions":["Regenerate the config update with a supported SDK/configtxlator so the ConsensusType message is correctly marshaled","Verify tooling and orderer are on compatible Hyperledger Fabric versions (protobuf schema alignment)","Inspect the config block with configtxlator to confirm the consensusType value decodes cleanly"],"exampleFix":"// before\ncv.Value = manuallySerializedBytes\n// after\nvalue, err := proto.Marshal(&orderer.ConsensusType{\n\tType: \"etcdraft\", Metadata: metadataBytes,\n})\nif err != nil { return err }\ncv.Value = value","handlingStrategy":"type-guard","validationCode":"// pre-validate the config value bytes before processing\ntest := &orderer.ConsensusType{}\nif err := proto.Unmarshal(configValue.GetValue(), test); err != nil {\n\treturn fmt.Errorf(\"consensusType value is not a valid ConsensusType protobuf: %w\", err)\n}","typeGuard":"func isConsensusTypeValue(b []byte) bool {\n\tct := &orderer.ConsensusType{}\n\treturn proto.Unmarshal(b, ct) == nil\n}","tryCatchPattern":"md, ct, err := MetadataFromConfigValue(configValue)\nif err != nil {\n\tif strings.Contains(err.Error(), \"failed to unmarshal consensusType config update\") {\n\t\treturn fmt.Errorf(\"malformed consensusType value — regenerate with configtxlator: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Generate config updates with supported SDKs/configtxlator, not hand-built bytes","Keep Fabric tooling and orderer versions aligned","Round-trip marshal/unmarshal in tests before submitting updates","Inspect stored config blocks with configtxlator when replay issues appear"],"tags":["raft","config","protobuf","unmarshal","validation"],"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"}