hyperledger/fabric · error

proposal payload cannot be nil

Error message

proposal payload cannot be nil

What it means

ProposalToBlock requires the proposal's Payload field, which holds the serialized (payload, metadata) tuple of the block. An empty Payload means the block's data cannot be reconstructed, so conversion fails with this error after the header checks pass.

Source

Thrown at orderer/consensus/smartbft/signature.go:73

	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 {
		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 {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Ensure the code constructing types.Proposal populates both Header and Payload (Payload from the proposal's marshalled block data+metadata tuple).
  2. Re-fetch the proposal by triggering node synchronization from other consenters.
  3. Check disk health and ledger/WAL integrity; restore from backup if records are truncated.
  4. Compare proposal bytes across consenters (signature verification logs) to locate the node with the incomplete proposal.

Example fix

// before
prop := types.Proposal{Header: headerBytes}
block, _ := ProposalToBlock(prop)

// after
prop := types.Proposal{Header: headerBytes, Payload: payloadBytes}
block, err := ProposalToBlock(prop)
Defensive patterns

Strategy: validation

Validate before calling

if len(prop.Payload) == 0 {
    return fmt.Errorf("proposal has no payload; cannot convert to block")
}
block, err := ProposalToBlock(prop)

Type guard

func hasProposalPayload(p *types.Proposal) bool {
    return p != nil && len(p.Payload) > 0
}

Try / catch

block, err := ProposalToBlock(prop)
if err != nil && err.Error() == "proposal payload cannot be nil" {
    // refetch proposal or trigger synchronization
    return resync()
}

Prevention

When it happens

Trigger: VerifyProposal/SignProposal/RequestsFromProposal/Deliver invoked with a types.Proposal that has a valid Header but an empty or nil Payload byte slice — e.g. a proposal constructed only from a header or truncated during persistence.

Common situations: Corrupted proposal entries after unclean shutdown; integration code assembling Proposal structs manually and forgetting to set Payload; truncated records read from storage after disk issues.

Related errors


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