hyperledger/fabric · error

transaction %d has no payload

Error message

transaction %d has no payload

What it means

VerifyTransactionsAreWellFormed validates each entry in a block's Data field by unmarshaling it into a common.Envelope. After a successful proto.Unmarshal, it checks that env.Payload is non-empty; a zero-length Payload means the envelope is structurally present but carries no transaction payload, so the block cannot be hashed/verified meaningfully. The library throws this to fail fast on corrupt or malformed block data rather than propagating an empty payload downstream.

Source

Thrown at protoutil/blockutils.go:334

func VerifyTransactionsAreWellFormed(bd *cb.BlockData) error {
	if bd == nil || bd.Data == nil || len(bd.Data) == 0 {
		return errors.New("empty block")
	}

	// If we have a single transaction, and the block is a config block, then no need to check
	// well formed-ness, because there cannot be another transaction in the original block.
	if HasConfigTx(bd) {
		return nil
	}

	for i, rawTx := range bd.Data {
		env := &cb.Envelope{}
		if err := proto.Unmarshal(rawTx, env); err != nil {
			return fmt.Errorf("transaction %d is invalid: %v", i, err)
		}

		if len(env.Payload) == 0 {
			return fmt.Errorf("transaction %d has no payload", i)
		}

		if len(env.Signature) == 0 {
			return fmt.Errorf("transaction %d has no signature", i)
		}

		expected, err := proto.Marshal(env)
		if err != nil {
			return fmt.Errorf("failed re-marshaling envelope: %v", err)
		}

		if len(expected) < len(rawTx) {
			return fmt.Errorf("transaction %d has %d trailing bytes", i, len(rawTx)-len(expected))
		}
		if !bytes.Equal(expected, rawTx) {
			return fmt.Errorf("transaction %d (%s) does not match its raw form (%s)", i,
				base64.StdEncoding.EncodeToString(expected), base64.StdEncoding.EncodeToString(rawTx))
		}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Inspect block.Data.Data[i] and confirm the envelope was produced with protoutil.Marshal/MarshalOrPanic of a fully populated common.Envelope (Payload and Signature set).
  2. Regenerate or re-fetch the block from a trusted peer/orderer instead of using the corrupt copy.
  3. In block-producing code, reject envelopes with empty Payload before adding them to a block.
  4. Log the offending index and dump base64 of the raw bytes to compare against a known-good envelope.

Example fix

// before: appending raw, possibly empty payload
env := &cb.Envelope{Signature: sig}
raw, _ := proto.Marshal(env)
// after
if len(env.Payload) == 0 {
	return errors.New("refusing to create envelope with empty payload")
}
raw, _ := proto.Marshal(env)
Defensive patterns

Strategy: validation

Validate before calling

for i, rawTx := range block.Data.Data {
	env := &cb.Envelope{}
	if err := proto.Unmarshal(rawTx, env); err != nil { return err }
	if len(env.Payload) == 0 {
		return fmt.Errorf("tx %d: empty payload, skipping block", i)
	}
}

Type guard

func hasPayload(env *cb.Envelope) bool { return env != nil && len(env.Payload) > 0 }

Prevention

When it happens

Trigger: Calling BlockDataHash (directly or indirectly) on a block whose Data contains an entry that unmarshals to an Envelope with len(env.Payload)==0 — e.g. a truncated, hand-crafted, or wrongly marshaled transaction at index i.

Common situations: Processing a corrupted or externally produced genesis/application block; writing blocks with test helpers that append raw bytes instead of marshaled envelopes; deserializing blocks from an untrusted or buggy source; block storage truncation after a crash.

Related errors


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