hyperledger/fabric · error
bad header
Error message
bad header
What it means
While converting a SmartBFT proposal to a block, the proposal Header bytes failed ASN.1 unmarshalling into the internal asn1Header struct. The header is present but not decodable, so the error wraps the asn1 failure with the context 'bad header'.
Source
Thrown at orderer/consensus/smartbft/signature.go:63
return msg2Sign
}
// ProposalToBlock marshals the proposal the block
func ProposalToBlock(proposal types.Proposal) (*cb.Block, error) {
// initialize block with empty fields
block := &cb.Block{
Data: &cb.BlockData{},
Metadata: &cb.BlockMetadata{},
}
if len(proposal.Header) == 0 {
return nil, errors.New("proposal header cannot be nil")
}
hdr := &asn1Header{}
if _, err := asn1.Unmarshal(proposal.Header, hdr); err != nil {
return nil, errors.Wrap(err, "bad header")
}
block.Header = &cb.BlockHeader{
Number: hdr.Number.Uint64(),
PreviousHash: hdr.PreviousHash,
DataHash: hdr.DataHash,
}
if len(proposal.Payload) == 0 {
return nil, errors.New("proposal payload cannot be nil")
}
tuple := &ByteBufferTuple{}
if err := tuple.FromBytes(proposal.Payload); err != nil {
return nil, errors.Wrap(err, "bad payload and metadata tuple")
}
if err := proto.Unmarshal(tuple.A, block.Data); err != nil {View on GitHub (pinned to 2736b63f8f)
Solutions
- Verify the Fabric binaries on all consenters are the same version (peer/orderer version alignment).
- Delete corrupted local state and re-sync the channel from other consenters (restart the orderer to trigger synchronization).
- Check storage integrity (WAL/ledger files) on the node; restore from a healthy snapshot/backup if corruption is confirmed.
- If the data came from the network, inspect whether a peer is sending malformed messages and verify TLS/cluster communication integrity.
Example fix
// before
// consuming a proposal from an old, incompatible WAL entry
prop := readFromWALUnchecked()
block, err := ProposalToBlock(prop)
// after
// validate source and re-sync if version skew suspected
if !sameFabricVersion(clusterPeers) {
return resyncFromQuorum()
}
block, err := ProposalToBlock(prop) Defensive patterns
Strategy: validation
Validate before calling
hdr := &asn1Header{}
if _, err := asn1.Unmarshal(prop.Header, hdr); err != nil {
return fmt.Errorf("proposal header not ASN.1 decodable: %v", err)
}
block, err := ProposalToBlock(prop) Type guard
func headerDecodable(p types.Proposal) bool {
hdr := &asn1Header{}
_, err := asn1.Unmarshal(p.Header, hdr)
return err == nil && hdr.Number != nil
} Try / catch
block, err := ProposalToBlock(prop)
if err != nil && strings.HasPrefix(err.Error(), "bad header") {
// treat proposal as corrupted: resync from quorum
return resync()
} Prevention
- Keep all orderer binaries at the same Fabric version.
- Verify checksums/backups of WAL and ledger data.
- Reject proposals whose provenance cannot be established (signature checks first).
- Test upgrades on a staging network to catch encoding changes.
When it happens
Trigger: VerifyProposal/SignProposal/RequestsFromProposal/Deliver called with a Proposal whose Header is corrupted, truncated, or was produced by an incompatible encoding version (not the ASN.1 asn1Header layout).
Common situations: Binary corruption during backup/restore or WAL replay; version skew between Fabric binaries after an upgrade where header encoding changed; hand-crafted or replayed messages injected by a malicious or buggy peer.
Related errors
- consenter options type mismatch
- failed parsing certificate %s
- parseCertificate failed
- failed to unmarshal BFT metadata configuration
- invalid BFT metadata configuration
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/24b148d3ffa7934d.
Report an issue: GitHub.