{"record":{"id":"c09cd3838b45d486","repo":"hyperledger/fabric","slug":"failed-to-unmarshal-consenter-metadata","errorCode":null,"errorMessage":"failed to unmarshal consenter metadata","messagePattern":"failed to unmarshal consenter metadata","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"protoutil/blockutils.go","lineNumber":175,"sourceCode":"\tif err != nil {\n\t\treturn nil, errors.WithMessage(err, \"failed to retrieve metadata\")\n\t}\n\n\t// TODO FAB-15864 Remove this fallback when we can stop supporting upgrade from pre-1.4.1 orderer\n\tif len(m.Value) == 0 {\n\t\treturn GetMetadataFromBlock(block, cb.BlockMetadataIndex_ORDERER)\n\t}\n\n\tobm := &cb.OrdererBlockMetadata{}\n\terr = proto.Unmarshal(m.Value, obm)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to unmarshal orderer block metadata\")\n\t}\n\n\tres := &cb.Metadata{}\n\terr = proto.Unmarshal(obm.ConsenterMetadata, res)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to unmarshal consenter metadata\")\n\t}\n\n\treturn res, nil\n}\n\n// GetLastConfigIndexFromBlock retrieves the index of the last config block as\n// encoded in the block metadata\nfunc GetLastConfigIndexFromBlock(block *cb.Block) (uint64, error) {\n\tm, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_SIGNATURES)\n\tif err != nil {\n\t\treturn 0, errors.WithMessage(err, \"failed to retrieve metadata\")\n\t}\n\t// TODO FAB-15864 Remove this fallback when we can stop supporting upgrade from pre-1.4.1 orderer\n\tif len(m.Value) == 0 {\n\t\tm, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIG)\n\t\tif err != nil {\n\t\t\treturn 0, errors.WithMessage(err, \"failed to retrieve metadata\")\n\t\t}","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/protoutil/blockutils.go#L157-L193","documentation":"GetConsenterMetadataFromBlock extracts the consenter metadata from a block's orderer metadata. After unmarshalling the OrdererBlockMetadata, it attempts to proto-unmarshal the ConsenterMetadata field into cb.Metadata; this error wraps any failure of that unmarshal, meaning the consenter metadata bytes are corrupt, empty-but-present, or not a valid protobuf Metadata message.","triggerScenarios":"Calling GetConsenterMetadataFromBlock on a block whose ORDERER metadata entry contains a ConsenterMetadata field that fails proto.Unmarshal — e.g. bytes written by a different metadata schema, truncated/corrupt block data, or a nil/empty ConsenterMetadata where the underlying proto unmarshal of malformed bytes fails.","commonSituations":"Blocks produced by mismatched ordering-service versions (BFT vs non-BFT metadata layouts), blocks copied or truncated during ledger migration/snapshot restore, or reading blocks from a foreign/incompatible channel genesis block.","solutions":["Verify the block metadata was produced by a compatible Fabric ordering-service version; regenerate or refetch the block from the ledger","Inspect obm.ConsenterMetadata bytes (length, leading protobuf field tags) to confirm they are a serialized cb.Metadata","If restoring from a snapshot/backup, re-fetch the config block from a live ordering node instead of the copy","Update Fabric binaries/protos so producer and consumer agree on the OrdererBlockMetadata schema"],"exampleFix":"// before\nres := &cb.Metadata{}\nif err := proto.Unmarshal(obm.ConsenterMetadata, res); err != nil {\n    return nil, errors.Wrap(err, \"failed to unmarshal consenter metadata\")\n}\n// after\nif len(obm.ConsenterMetadata) == 0 {\n    return nil, errors.New(\"consenter metadata is absent in orderer block metadata\")\n}\nres := &cb.Metadata{}\nif err := proto.Unmarshal(obm.ConsenterMetadata, res); err != nil {\n    logger.Errorf(\"consenter metadata bytes (%d) not a valid cb.Metadata: %v\", len(obm.ConsenterMetadata), err)\n    return nil, errors.Wrap(err, \"failed to unmarshal consenter metadata\")\n}","handlingStrategy":"validation","validationCode":"func hasConsenterMetadata(block *cb.Block) bool {\n    md := block.GetMetadata().GetMetadata()\n    if len(md) < int(cb.BlockMetadataIndex_ORDERER)+1 || len(md[cb.BlockMetadataIndex_ORDERER]) == 0 {\n        return false\n    }\n    obm := &cb.OrdererBlockMetadata{}\n    if proto.Unmarshal(md[cb.BlockMetadataIndex_ORDERER], obm) != nil {\n        return false\n    }\n    return len(obm.GetConsenterMetadata()) > 0\n}","typeGuard":"func isParseableMetadata(raw []byte) (*cb.Metadata, bool) {\n    m := &cb.Metadata{}\n    if len(raw) == 0 || proto.Unmarshal(raw, m) != nil {\n        return nil, false\n    }\n    return m, true\n}","tryCatchPattern":"res, err := GetConsenterMetadataFromBlock(block)\nif err != nil {\n    logger.Warnf(\"skipping block %d, bad consenter metadata: %v\", block.GetHeader().GetNumber(), err)\n    return nil // or re-fetch the block\n}","preventionTips":["Keep all Fabric ordering/peer binaries on the same version so OrdererBlockMetadata schemas match","Never hand-edit or truncate block files; use ledger tooling for migration","Check metadata entry length before unmarshalling","Re-fetch blocks from a live ordering node when parsing fails"],"tags":["protobuf","unmarshal","block-metadata","hyperledger-fabric"],"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"}