hyperledger/fabric · error

block data is nil

Error message

block data is nil

What it means

NewChain requires the join block to carry Data because it derives configuration and endpoints (for the block puller) from the block's payload. A block with a Header but nil Data is incomplete and cannot be used to bootstrap the follower, so creation fails.

Source

Thrown at orderer/common/follower/follower_chain.go:183

		}
		if err := blockPullerFactory.UpdateVerifierFromConfigBlock(chain.lastConfig); err != nil {
			return nil, err
		}
	}

	if joinBlock == nil {
		chain.status = types.StatusActive
		if isMem, _ := chain.clusterConsenter.IsChannelMember(chain.lastConfig); isMem {
			chain.consensusRelation = types.ConsensusRelationConsenter
		}

		chain.logger.Infof("Created with a nil join-block, ledger height: %d", chain.firstHeight)
	} else {
		if joinBlock.Header == nil {
			return nil, errors.New("block header is nil")
		}
		if joinBlock.Data == nil {
			return nil, errors.New("block data is nil")
		}

		// Check the block puller creation function once before we start the follower. This ensures we can extract
		// the endpoints from the join-block.
		puller, err := blockPullerFactory.BlockPuller(joinBlock, nil)
		if err != nil {
			return nil, errors.WithMessage(err, "error creating a block puller from join-block")
		}
		puller.Close()

		if chain.joinBlock.Header.Number < chain.ledgerResources.Height() {
			chain.status = types.StatusActive
		}
		if isMem, _ := chain.clusterConsenter.IsChannelMember(chain.joinBlock); isMem {
			chain.consensusRelation = types.ConsensusRelationConsenter
		}

		chain.logger.Infof("Created with join-block number: %d, ledger height: %d", joinBlock.Header.Number, chain.firstHeight)

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Provide a complete block (Header + Data + Metadata), typically a config block, as the join block
  2. Re-obtain the join block from a healthy orderer or the channel's genesis/config block source
  3. Check unmarshal errors when deserializing the join block; do not ignore partial failures
  4. If hand-constructing, populate Data with the marshaled Payload of the config block

Example fix

// before
joinBlock := &common.Block{Header: &common.BlockHeader{Number: 5}}
chain, err := follower.NewChain(..., joinBlock, ...)
// after
env := configBlockEnvelope // config envelope from a valid source
payloadMarshal, _ := proto.Marshal(env)
data, _ := proto.Marshal(&common.Payload{Header: hdr, Data: payloadMarshal})
joinBlock := &common.Block{Header: &common.BlockHeader{Number: 5}, Data: &common.BlockData{Data: [][]byte{data}}}
chain, err := follower.NewChain(..., joinBlock, ...)
Defensive patterns

Strategy: validation

Validate before calling

func validJoinBlock(b *common.Block) error {
    if b == nil || b.Header == nil {
        return errors.New("join block missing header")
    }
    if b.Data == nil || len(b.Data.Data) == 0 {
        return errors.New("join block has no data payload")
    }
    return nil
}
// run before follower.NewChain(..., joinBlock, ...)

Type guard

func hasPayload(b *common.Block) bool {
    return b != nil && b.Data != nil && len(b.Data.Data) > 0
}

Try / catch

chain, err := follower.NewChain(..., joinBlock, ...)
if err != nil {
    if err.Error() == "block data is nil" {
        return fmt.Errorf("join block lacks data payload; use a full config block")
    }
    return err
}

Prevention

When it happens

Trigger: Calling NewChain with a joinBlock that has a Header but nil Data — e.g. a block assembled from only a header, a corrupted payload, or unmarshaling that partially failed.

Common situations: Hand-crafted join blocks in tests missing Data; truncated block files used with osnadmin join; snapshot/restore tooling writing header-only blocks; corruption during transfer of the join block.

Related errors


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