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
- 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).
- Regenerate or re-fetch the block from a trusted peer/orderer instead of using the corrupt copy.
- In block-producing code, reject envelopes with empty Payload before adding them to a block.
- 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
- Always build envelopes via protoutil helpers rather than hand-assembling bytes.
- Validate envelope contents before inserting into a block.
- Check block provenance; never trust externally generated block files.
- Log the offending transaction index when validation fails.
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
- transaction %d has no signature
- transaction %d has %d trailing bytes
- transaction %d (%s) does not match its raw form (%s)
- malformed org definition for org: %s
- error encode input
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/bf8b8f53ca52b201.
Report an issue: GitHub.