hyperledger/fabric · error
block header is nil
Error message
block header is nil
What it means
NewChain validates the join block when one is supplied (i.e. joining at a non-genesis block). If joinBlock.Header is nil the block is structurally unusable — the follower needs the header to determine height, configuration, and endpoints — so creation fails immediately.
Source
Thrown at orderer/common/follower/follower_chain.go:180
if ledgerResources.Height() > 0 {
if err := chain.loadLastConfig(); err != nil {
return nil, err
}
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.ConsensusRelationConsenterView on GitHub (pinned to 2736b63f8f)
Solutions
- Verify the join block file is a valid, complete block (check protoutil.Unmarshal / unmarshal error handling)
- Re-export or re-obtain the join block from a valid source (config block from genesis or an orderer)
- Initialize Header and Data on programmatically constructed join blocks
- Confirm the block was correctly read from disk (size > 0, not truncated)
Example fix
// before
var joinBlock *common.Block
proto.Unmarshal(data, joinBlock) // error ignored -> nil Header
chain, err := follower.NewChain(..., joinBlock, ...)
// after
joinBlock := &common.Block{}
if err := proto.Unmarshal(data, joinBlock); err != nil {
return nil, errors.Wrap(err, "failed unmarshaling join block")
}
if joinBlock.Header == nil {
return nil, errors.New("join block has nil header")
}
chain, err := follower.NewChain(..., joinBlock, ...) Defensive patterns
Strategy: validation
Validate before calling
func validJoinBlock(b *common.Block) error {
if b == nil {
return errors.New("join block is nil")
}
if b.Header == nil {
return errors.New("join block header is nil")
}
if b.Data == nil {
return errors.New("join block data is nil")
}
return nil
}
// run before follower.NewChain(..., joinBlock, ...) Type guard
func hasHeaderAndData(b *common.Block) bool {
return b != nil && b.Header != nil && b.Data != nil
} Try / catch
chain, err := follower.NewChain(..., joinBlock, ...)
if err != nil {
if err.Error() == "block header is nil" {
return fmt.Errorf("invalid join block: re-obtain a complete config block")
}
return err
} Prevention
- Check errors from block unmarshaling before passing the block as JoinBlock
- Validate join-block files with protoutil before osnadmin channel join
- Source join blocks from trusted config blocks, not ad-hoc files
- Keep join-block files intact; verify size and readability before use
When it happens
Trigger: Calling NewChain (via createFollower or channel participation API 'join' with a join-block) with a joinBlock whose Header field is nil: unmarshaled from corrupt/empty bytes, wrong file provided, or JoinBlock wiring producing an empty block.
Common situations: osnadmin channel join supplying a malformed join block file; ledger snapshot/bootstrap code passing a block that failed unmarshaling silently; programming error constructing the JoinBlock in tests.
Related errors
- block data is nil
- %s is mandatory and cannot be empty
- enrollment certificate isn't a valid PEM block
- failed to decode PEM block from %s
- '%s' not equal <newest|oldest|config|(number)>
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/e7582bebc01e70eb.
Report an issue: GitHub.