hyperledger/fabric · error
failed unmarshalling identifier header for block %d: %v
Error message
failed unmarshalling identifier header for block %d: %v
What it means
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.
Source
Thrown at protoutil/blockutils.go:270
return func(header *cb.BlockHeader, metadata *cb.BlockMetadata) error {
if len(metadata.GetMetadata()) < int(cb.BlockMetadataIndex_SIGNATURES)+1 {
return errors.Errorf("no signatures in block metadata")
}
md := &cb.Metadata{}
if err := proto.Unmarshal(metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES], md); err != nil {
return errors.Wrapf(err, "error unmarshalling signatures from metadata: %v", err)
}
var signatureSet []*SignedData
for _, metadataSignature := range md.Signatures {
var signerIdentity []byte
var signedPayload []byte
// if the SignatureHeader is empty and the IdentifierHeader is present, then the consenter expects us to fetch its identity by its numeric identifier
if bftEnabled && len(metadataSignature.GetSignatureHeader()) == 0 && len(metadataSignature.GetIdentifierHeader()) > 0 {
identifierHeader, err := UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)
if err != nil {
return fmt.Errorf("failed unmarshalling identifier header for block %d: %v", header.GetNumber(), err)
}
identifier := identifierHeader.GetIdentifier()
signerIdentity = searchConsenterIdentityByID(consenters, identifier)
if len(signerIdentity) == 0 {
// The identifier is not within the consenter set
continue
}
signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.IdentifierHeader, BlockHeaderBytes(header))
} else {
signatureHeader, err := UnmarshalSignatureHeader(metadataSignature.GetSignatureHeader())
if err != nil {
return fmt.Errorf("failed unmarshalling signature header for block %d: %v", header.GetNumber(), err)
}
signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.SignatureHeader, BlockHeaderBytes(header))
signerIdentity = signatureHeader.Creator
}View on GitHub (pinned to 2736b63f8f)
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
Example fix
// before
identifierHeader, err := UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)
if err != nil {
return fmt.Errorf("failed unmarshalling identifier header for block %d: %v", header.GetNumber(), err)
}
// after
if len(metadataSignature.IdentifierHeader) == 0 {
return fmt.Errorf("block %d: BFT signature has empty identifier header", header.GetNumber())
}
identifierHeader, err := UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)
if err != nil {
return fmt.Errorf("failed unmarshalling identifier header for block %d: %v", header.GetNumber(), err)
} Defensive patterns
Strategy: validation
Validate before calling
if bftEnabled {
for _, s := range md.Signatures {
if len(s.GetSignatureHeader()) == 0 && len(s.GetIdentifierHeader()) > 0 {
ih := &ab.IdentifierHeader{} // probe parse
if proto.Unmarshal(s.GetIdentifierHeader(), ih) != nil {
return errors.New("malformed identifier header; reject block")
}
}
}
} Type guard
func isParseableIdentifierHeader(raw []byte) bool {
ih := &cb.IdentifierHeader{}
return len(raw) > 0 && proto.Unmarshal(raw, ih) == nil && ih.GetIdentifier() > 0
} Try / catch
if err := verifier(header, metadata); err != nil {
return fmt.Errorf("BFT block %d rejected: %w", header.GetNumber(), err)
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: SmartBFT ordering with version-skewed consenter protos, corrupted blocks delivered via gossip, or test blocks hand-assembled with invalid IdentifierHeader bytes.
Understand the failure class
Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.
Related errors
- failed unmarshalling signature header for block %d: %v
- error unmarshalling signatures from metadata: %v
- too few arguments
- Failed opening file %s: %v
- config isn't valid
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/081c9572750a27f4.
Report an issue: GitHub.