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

  1. Confirm all ordering nodes run the same SmartBFT-capable Fabric version so IdentifierHeader serialization matches
  2. Re-fetch the block from a trusted source; reject the corrupt block
  3. Validate IdentifierHeader bytes (must unmarshal to a message with an identifier field) before processing
  4. 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

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


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/081c9572750a27f4. Report an issue: GitHub.