hyperledger/fabric · error

block must be different from nil, channel=%s

Error message

block must be different from nil, channel=%s

What it means

verifyHeader is the first check in block chain verification: a nil block cannot be verified against the expected sequence number (lastBlockHeader.Number + 1), so it is rejected immediately with the channel name in the message.

Source

Thrown at common/deliverclient/block_verification.go:313

// UpdateBlockHeader saves the last block header that was verified and handled successfully.
// This must be called after VerifyBlock and VerifyBlockAttestation and successfully handling the block.
func (a *BlockVerificationAssistant) UpdateBlockHeader(block *common.Block) {
	a.lastBlockHeader = block.Header
	a.lastBlockHeaderHash = protoutil.BlockHeaderHash(block.Header)
}

func (a *BlockVerificationAssistant) verifyMetadata(block *common.Block) error {
	if block.Metadata == nil || len(block.Metadata.Metadata) < len(common.BlockMetadataIndex_name) {
		return errors.Errorf("block with id [%d] on channel [%s] does not have metadata or contains too few entries", block.Header.Number, a.channelID)
	}

	return nil
}

func (a *BlockVerificationAssistant) verifyHeader(block *common.Block) error {
	if block == nil {
		return errors.Errorf("block must be different from nil, channel=%s", a.channelID)
	}
	if block.Header == nil {
		return errors.Errorf("invalid block, header must be different from nil, channel=%s", a.channelID)
	}

	expectedBlockNum := a.lastBlockHeader.Number + 1
	if expectedBlockNum != block.Header.Number {
		return errors.Errorf("expected block number is [%d] but actual block number inside block is [%d]", expectedBlockNum, block.Header.Number)
	}

	if len(a.lastBlockHeaderHash) != 0 {
		if !bytes.Equal(block.Header.PreviousHash, a.lastBlockHeaderHash) {
			return errors.Errorf("Header.PreviousHash of block [%d] is different from Hash(block.Header) of previous block, on channel [%s], received: %s, expected: %s",
				block.Header.Number, a.channelID, hex.EncodeToString(block.Header.PreviousHash), hex.EncodeToString(a.lastBlockHeaderHash))
		}
	}
	return nil
}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Guard the caller: check block != nil (and block.Header != nil) before calling VerifyBlock/VerifyBlockAttestation.
  2. Check why the deliver stream produced a nil block — inspect the received DeliverResponse/Envelope unmarshalling errors.
  3. Log the delivery response error instead of discarding it before invoking verification.

Example fix

// before
if err := bva.VerifyBlock(recvBlock, cryptoOpts); err != nil {...}

// after
if recvBlock == nil || recvBlock.Header == nil {
    return fmt.Errorf("no block received from orderer")
}
if err := bva.VerifyBlock(recvBlock, cryptoOpts); err != nil {...}
Defensive patterns

Strategy: type-guard

Validate before calling

if block == nil || block.Header == nil {
    return errors.New("deliver response contained no block")
}

Type guard

func isVerifiableBlock(block *common.Block) bool {
    return block != nil && block.Header != nil
}

Try / catch

resp, err := deliverStream.Recv()
if err != nil { return err }
block, err := protoutil.UnmarshalBlock(resp.GetBlock())
if err != nil || block == nil { return fmt.Errorf("failed to decode delivered block: %w", err) }
return bva.VerifyBlock(block, opts)

Prevention

When it happens

Trigger: VerifyBlock or VerifyBlockAttestation invoked with a nil *common.Block — typically a deliver client callback received a nil block (failed unmarshalling of the delivered message, or a placeholder nil passed in custom code).

Common situations: Custom deliver handlers passing the block variable without checking it after a failed/unsuccessful delivery; mocks in tests returning nil; a nil result from a decode helper silently propagated.

Related errors


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