{"record":{"id":"081c9572750a27f4","repo":"hyperledger/fabric","slug":"failed-unmarshalling-identifier-header-for-block","errorCode":null,"errorMessage":"failed unmarshalling identifier header for block %d: %v","messagePattern":"failed unmarshalling identifier header for block (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"protoutil/blockutils.go","lineNumber":270,"sourceCode":"\treturn func(header *cb.BlockHeader, metadata *cb.BlockMetadata) error {\n\t\tif len(metadata.GetMetadata()) < int(cb.BlockMetadataIndex_SIGNATURES)+1 {\n\t\t\treturn errors.Errorf(\"no signatures in block metadata\")\n\t\t}\n\n\t\tmd := &cb.Metadata{}\n\t\tif err := proto.Unmarshal(metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES], md); err != nil {\n\t\t\treturn errors.Wrapf(err, \"error unmarshalling signatures from metadata: %v\", err)\n\t\t}\n\n\t\tvar signatureSet []*SignedData\n\t\tfor _, metadataSignature := range md.Signatures {\n\t\t\tvar signerIdentity []byte\n\t\t\tvar signedPayload []byte\n\t\t\t// if the SignatureHeader is empty and the IdentifierHeader is present, then  the consenter expects us to fetch its identity by its numeric identifier\n\t\t\tif bftEnabled && len(metadataSignature.GetSignatureHeader()) == 0 && len(metadataSignature.GetIdentifierHeader()) > 0 {\n\t\t\t\tidentifierHeader, err := UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed unmarshalling identifier header for block %d: %v\", header.GetNumber(), err)\n\t\t\t\t}\n\t\t\t\tidentifier := identifierHeader.GetIdentifier()\n\t\t\t\tsignerIdentity = searchConsenterIdentityByID(consenters, identifier)\n\t\t\t\tif len(signerIdentity) == 0 {\n\t\t\t\t\t// The identifier is not within the consenter set\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tsignedPayload = util.ConcatenateBytes(md.Value, metadataSignature.IdentifierHeader, BlockHeaderBytes(header))\n\t\t\t} else {\n\t\t\t\tsignatureHeader, err := UnmarshalSignatureHeader(metadataSignature.GetSignatureHeader())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed unmarshalling signature header for block %d: %v\", header.GetNumber(), err)\n\t\t\t\t}\n\n\t\t\t\tsignedPayload = util.ConcatenateBytes(md.Value, metadataSignature.SignatureHeader, BlockHeaderBytes(header))\n\n\t\t\t\tsignerIdentity = signatureHeader.Creator\n\t\t\t}","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/protoutil/blockutils.go#L252-L288","documentation":"In BFT mode, when a metadata signature carries an IdentifierHeader instead of a SignatureHeader, the verifier unmarshals the IdentifierHeader to look up the consenter's identity by numeric ID. Failure to unmarshal produces this formatted error, including the block number for traceability.","triggerScenarios":"Calling the BlockSignatureVerifier func with bftEnabled=true on a block whose signature entries have empty SignatureHeader but a non-empty, malformed IdentifierHeader (corrupt bytes or wrong schema).","commonSituations":"SmartBFT ordering with version-skewed consenter protos, corrupted blocks delivered via gossip, or test blocks hand-assembled with invalid IdentifierHeader bytes.","solutions":["Confirm all ordering nodes run the same SmartBFT-capable Fabric version so IdentifierHeader serialization matches","Re-fetch the block from a trusted source; reject the corrupt block","Validate IdentifierHeader bytes (must unmarshal to a message with an identifier field) before processing","If not running BFT, ensure bftEnabled is set correctly so the wrong decode path is not taken"],"exampleFix":"// before\nidentifierHeader, err := UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)\nif err != nil {\n    return fmt.Errorf(\"failed unmarshalling identifier header for block %d: %v\", header.GetNumber(), err)\n}\n// after\nif len(metadataSignature.IdentifierHeader) == 0 {\n    return fmt.Errorf(\"block %d: BFT signature has empty identifier header\", header.GetNumber())\n}\nidentifierHeader, err := UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)\nif err != nil {\n    return fmt.Errorf(\"failed unmarshalling identifier header for block %d: %v\", header.GetNumber(), err)\n}","handlingStrategy":"validation","validationCode":"if bftEnabled {\n    for _, s := range md.Signatures {\n        if len(s.GetSignatureHeader()) == 0 && len(s.GetIdentifierHeader()) > 0 {\n            ih := &ab.IdentifierHeader{} // probe parse\n            if proto.Unmarshal(s.GetIdentifierHeader(), ih) != nil {\n                return errors.New(\"malformed identifier header; reject block\")\n            }\n        }\n    }\n}","typeGuard":"func isParseableIdentifierHeader(raw []byte) bool {\n    ih := &cb.IdentifierHeader{}\n    return len(raw) > 0 && proto.Unmarshal(raw, ih) == nil && ih.GetIdentifier() > 0\n}","tryCatchPattern":"if err := verifier(header, metadata); err != nil {\n    return fmt.Errorf(\"BFT block %d rejected: %w\", header.GetNumber(), err)\n}","preventionTips":["Run identical SmartBFT Fabric versions on all ordering nodes","Confirm bftEnabled reflects the channel's consensus type before verification","Reject blocks with empty identifier headers early","Monitor gossip/orderer delivery for repeated corruption"],"tags":["bft","identifier-header","unmarshal","block-verification"],"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"}