hyperledger/fabric · error

block data is nil

Error message

block data is nil

What it means

ExtractEnvelope returns this error when the supplied *cb.Block has a nil Data field, i.e. the block carries no transaction data section at all. A well-formed Fabric block always has a non-nil Data (possibly empty), so a nil Data indicates an uninitialized or malformed block struct.

Source

Thrown at protoutil/commonutils.go:97

	err = errors.Wrapf(err, "error unmarshalling message for type %s", headerType)
	return chdr, err
}

// ExtractEnvelopeOrPanic retrieves the requested envelope from a given block
// and unmarshals it -- it panics if either of these operations fail
func ExtractEnvelopeOrPanic(block *cb.Block, index int) *cb.Envelope {
	envelope, err := ExtractEnvelope(block, index)
	if err != nil {
		panic(err)
	}
	return envelope
}

// ExtractEnvelope retrieves the requested envelope from a given block and
// unmarshals it
func ExtractEnvelope(block *cb.Block, index int) (*cb.Envelope, error) {
	if block.Data == nil {
		return nil, errors.New("block data is nil")
	}

	envelopeCount := len(block.Data.Data)
	if index < 0 || index >= envelopeCount {
		return nil, errors.New("envelope index out of bounds")
	}
	marshaledEnvelope := block.Data.Data[index]
	envelope, err := GetEnvelopeFromBlock(marshaledEnvelope)
	err = errors.WithMessagef(err, "block data does not carry an envelope at index %d", index)
	return envelope, err
}

// MakeChannelHeader creates a ChannelHeader.
func MakeChannelHeader(headerType cb.HeaderType, version int32, chainID string, epoch uint64) *cb.ChannelHeader {
	tm := timestamppb.Now()
	tm.Nanos = 0
	return &cb.ChannelHeader{
		Type:      int32(headerType),

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Check block.Data for nil (and len(block.Data.Data)) before calling ExtractEnvelope, or use IsConfigBlock-style nil checks.
  2. Trace where the block was produced/decoded — fix the source to always populate Data (e.g. use protoutil.NewBlock).
  3. If read from disk, validate the ledger storage; a truncated serialization can yield a Data-less block.
  4. For genesis-like empty blocks, special-case index access instead of calling ExtractEnvelope.

Example fix

// before
env, err := protoutil.ExtractEnvelope(block, 0) // 'block data is nil' on empty struct
// after
if block.Data == nil || len(block.Data.Data) == 0 {
    return fmt.Errorf("block %d has no data", block.Header.Number)
}
env, err := protoutil.ExtractEnvelope(block, 0)
Defensive patterns

Strategy: validation

Validate before calling

if block == nil || block.Data == nil {
    return errors.New("refusing to extract: block has nil Data")
}
env, err := protoutil.ExtractEnvelope(block, index)

Type guard

func blockHasData(block *cb.Block) bool {
    return block != nil && block.Data != nil
}

Try / catch

env, err := protoutil.ExtractEnvelope(block, 0)
if err != nil {
    if err.Error() == "block data is nil" {
        return fmt.Errorf("block %d is malformed (no data section)", block.GetHeader().GetNumber())
    }
    return err
}

Prevention

When it happens

Trigger: Passing a *cb.Block created as &cb.Block{} or decoded from an empty/truncated byte slice to ExtractEnvelope, before the len() bounds check can run.

Common situations: Hand-constructed blocks in tests, blocks deserialized from a corrupted file ledger, APIs that return a bare Block without data on failure, block protos partially populated by external tooling.

Related errors


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