hyperledger/fabric · error

last config is nil

Error message

last config is nil

What it means

SmartBFT blocks carry OrdererBlockMetadata inside the block's orderer metadata, including a LastConfig index pointing at the block number of the most recent config block. verifyBlockDataAndMetadata computes the expected last-config index (persisted or, for config blocks, the block's own number) and first checks that the signed metadata actually contains a LastConfig. This error means the OrdererBlockMetadata signature metadata has a nil LastConfig field, so the verifier cannot validate the block's config lineage.

Source

Thrown at orderer/consensus/smartbft/verifier.go:311

	if !proto.Equal(metadataInBlock, metadataFromProposal) {
		return nil, errors.Errorf(
			"expected metadata in block to be [view_id:%d latest_sequence:%d] but got [view_id:%d latest_sequence:%d]",
			metadataFromProposal.GetViewId(), metadataFromProposal.GetLatestSequence(),
			metadataInBlock.GetViewId(), metadataInBlock.GetLatestSequence(),
		)
	}

	rtc := v.RuntimeConfig.Load().(RuntimeConfig)
	lastConfig := rtc.LastConfigBlock.Header.Number

	if protoutil.IsConfigBlock(block) {
		lastConfig = block.Header.Number
	}

	// Verify last config
	if ordererMetadataFromSignature.LastConfig == nil {
		return nil, errors.Errorf("last config is nil")
	}

	if ordererMetadataFromSignature.LastConfig.Index != lastConfig {
		return nil, errors.Errorf("last config in block orderer metadata points to %d but our persisted last config is %d", ordererMetadataFromSignature.LastConfig.Index, lastConfig)
	}

	rawLastConfig, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIG)
	if err != nil {
		return nil, err
	}
	lastConf := &cb.LastConfig{}
	if err := proto.Unmarshal(rawLastConfig.Value, lastConf); err != nil {
		return nil, err
	}
	if lastConf.Index != lastConfig {
		return nil, errors.Errorf("last config in block metadata points to %d but our persisted last config is %d", ordererMetadataFromSignature.LastConfig.Index, lastConfig)
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Verify all ordering nodes run a Fabric version that populates OrdererBlockMetadata.LastConfig and upgrade any outdated nodes.
  2. Force a view change / re-proposal so a healthy leader rebuilds the block with complete orderer metadata.
  3. Inspect the offending block's metadata for corruption; restore the ledger from a snapshot if bytes are malformed.
  4. If this occurs during channel creation or genesis processing, regenerate the genesis block with the standard configtxgen tooling instead of custom-built blocks.

Example fix

// before: block built without last-config field
OrdererBlockMetadata{ConsenterMetadata: md}              // LastConfig nil -> error
// after: proposer always sets it
OrdererBlockMetadata{ConsenterMetadata: md, LastConfig: &cb.LastConfig{Index: lastConfigBlockNumber}}
Defensive patterns

Strategy: validation

Validate before calling

omd := &common.OrdererBlockMetadata{}
if err := proto.Unmarshal(ordererMDBytes, omd); err != nil { return err }
if omd.GetLastConfig() == nil {
    return errors.New("OrdererBlockMetadata missing LastConfig")
}

Try / catch

if err := verifyProposal(prop); err != nil {
    if strings.Contains(err.Error(), "last config is nil") {
        // drop proposal, request re-proposal / view change
    }
    return err
}

Prevention

When it happens

Trigger: VerifyProposal -> verifyBlockDataAndMetadata: for a block being verified, ordererMetadataFromSignature.LastConfig is nil (the proto field was never populated by the proposer), regardless of whether the block is a config or normal block.

Common situations: A proposer built the block with an older/different code path that omits LastConfig; corrupted block metadata bytes; a hand-crafted or replayed proposal from a non-standard tool; mixed Fabric versions where the LastConfig field is not set.

Related errors


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