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

  1. Wrap the block payload correctly: encode (blockData, blockMetadata) with ByteBufferTuple.ToBytes before assigning Proposal.Payload.
  2. Re-sync the proposal from other consenters if the local bytes are corrupted.
  3. Align Fabric versions across all orderer nodes so the tuple encoding matches.
  4. 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

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


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