hyperledger/fabric · warning

payload header is nil

Error message

payload header is nil

What it means

getTxInfoFromTransactionBytes unwraps the envelope's payload and requires payload.Header to extract the channel header and compute the tx id. A payload without a header is invalid — the tx cannot be identified, so the function logs a warning and returns this error, causing the tx to be skipped from the block's private-data info.

Source

Thrown at gossip/privdata/coordinator.go:425

	txRWSet      *rwsetutil.TxRwSet
}

// getTxInfoFromTransactionBytes parses a transaction and returns info required for private data retrieval
func getTxInfoFromTransactionBytes(envBytes []byte) (*txInfo, error) {
	txInfo := &txInfo{}
	env, err := protoutil.GetEnvelopeFromBlock(envBytes)
	if err != nil {
		logger.Warningf("Invalid envelope: %s", err)
		return nil, err
	}

	payload, err := protoutil.UnmarshalPayload(env.Payload)
	if err != nil {
		logger.Warningf("Invalid payload: %s", err)
		return nil, err
	}
	if payload.Header == nil {
		err := errors.New("payload header is nil")
		logger.Warningf("Invalid tx: %s", err)
		return nil, err
	}

	chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
	if err != nil {
		logger.Warningf("Invalid channel header: %s", err)
		return nil, err
	}
	txInfo.channelID = chdr.ChannelId
	txInfo.txID = chdr.TxId

	if chdr.Type != int32(common.HeaderType_ENDORSER_TRANSACTION) {
		err := errors.New("header type is not an endorser transaction")
		logger.Debugf("Invalid transaction type: %s", err)
		return nil, err
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Obtain blocks only from trusted sources (orderer / validated peers) so envelopes carry proper headers
  2. Drop or re-fetch the malformed block; fabric skips the offending tx but repeated occurrences indicate a corrupt source
  3. Fix envelope construction to include payload.Header (ChannelHeader + SignatureHeader) in tests/tools
  4. Validate envelopes (protoutil.UnmarshalEnvelope + header presence) before ingesting

Example fix

// before
env.Payload = marshal(&common.Payload{Data: txBytes}) // no header
// after
env.Payload = marshal(&common.Payload{
    Header: &common.PayloadHeader{
        ChannelHeader:   marshal(chdr),
        SignatureHeader: marshal(shdr),
    },
    Data: txBytes,
})
Defensive patterns

Strategy: validation

Validate before calling

env, err := protoutil.UnmarshalEnvelope(txEnvBytes)
if err != nil { return err }
payload, err := protoutil.UnmarshalPayload(env.Payload)
if err != nil { return err }
if payload.Header == nil {
    return errors.New("envelope payload has no header; skipping tx")
}

Type guard

func envelopeHasHeader(txEnvBytes []byte) bool {
    env, err := protoutil.UnmarshalEnvelope(txEnvBytes)
    if err != nil { return false }
    payload, err := protoutil.UnmarshalPayload(env.Payload)
    if err != nil { return false }
    return payload.Header != nil
}

Prevention

When it happens

Trigger: StoreBlock -> getTxPvtdataInfoFromBlock -> getTxInfoFromTransactionBytes where a tx envelope's payload unmarshals fine but its Header field is nil — a malformed or maliciously crafted envelope inside block.Data.Data.

Common situations: Corrupted blocks from an untrusted gossip source; envelopes constructed manually in tests without headers; garbage bytes that happen to decode as a payload; truncated chaincode submission payloads.

Related errors


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