hyperledger/fabric · error
bad payload and metadata tuple
Error message
bad payload and metadata tuple
What it means
The proposal Payload is decoded as a ByteBufferTuple (two length-prefixed byte buffers: A=block data, B=block metadata). This error wraps any failure of that tuple decoding, meaning the Payload bytes are not in the expected tuple format.
Source
Thrown at orderer/consensus/smartbft/signature.go:78
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 {
return nil, errors.Wrap(err, "bad payload")
}
if err := proto.Unmarshal(tuple.B, block.Metadata); err != nil {
return nil, errors.Wrap(err, "bad metadata")
}
return block, nil
}
type asn1Header struct {
Number *big.Int
PreviousHash []byte
DataHash []byte
}
View on GitHub (pinned to 2736b63f8f)
Solutions
- Wrap the block payload correctly: encode (blockData, blockMetadata) with ByteBufferTuple.ToBytes before assigning Proposal.Payload.
- Re-sync the proposal from other consenters if the local bytes are corrupted.
- Align Fabric versions across all orderer nodes so the tuple encoding matches.
- Log/inspect the first bytes of Payload to confirm whether it is tuple-encoded before conversion.
Example fix
// before
prop.Payload = protoutil.MarshalOrPanic(block.Data)
// after
tuple := &ByteBufferTuple{A: marshalledData, B: marshalledMetadata}
prop.Payload = tuple.Bytes() Defensive patterns
Strategy: validation
Validate before calling
tuple := &ByteBufferTuple{}
if err := tuple.FromBytes(prop.Payload); err != nil {
return fmt.Errorf("payload not a valid tuple: %v", err)
}
block, err := ProposalToBlock(prop) Type guard
func payloadIsTuple(p types.Proposal) bool {
t := &ByteBufferTuple{}
return t.FromBytes(p.Payload) == nil
} Try / catch
block, err := ProposalToBlock(prop)
if err != nil && strings.HasPrefix(err.Error(), "bad payload and metadata tuple") {
// payload encoding mismatch: rebuild tuple or resync
return rebuildOrResync(prop)
} Prevention
- Use ByteBufferTuple.ToBytes (not raw marshalling) when filling Proposal.Payload.
- Keep Fabric versions aligned across the cluster.
- Unit-test proposal encode/decode round-trips in integrations.
- Validate tuple layout before persisting custom proposals.
When it happens
Trigger: ProposalToBlock called with a Payload that is not a valid ByteBufferTuple — e.g. raw protobuf block data passed directly instead of the tuple-encoded payload, or Payload bytes corrupted/offset.
Common situations: Custom integration code marshalling block data without the ByteBufferTuple wrapper before assigning to Proposal.Payload; byte-offset corruption in storage; messages produced by a mismatched Fabric version using a different payload encoding.
Related errors
- consenter options type mismatch
- could not turn parse profile into channel group
- failed to unmarshal BFT metadata configuration
- invalid BFT metadata configuration
- invalid BFT consenter mapping configuration
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/c0f1dab86699c2c8.
Report an issue: GitHub.