hyperledger/fabric · error

last config in block metadata points to %d but our persisted

Error message

last config in block metadata points to %d but our persisted last config is %d

What it means

Besides the signed OrdererBlockMetadata, the block itself embeds a LAST_CONFIG entry in its metadata whose LastConfig.Index must match the computed last config. This error means the LAST_CONFIG metadata inside the block points to a different block number than the persisted/signed last config. Note the error message uses the signed metadata's index in the text, which can look confusing — the failing comparison is lastConf.Index != lastConfig.

Source

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

	// 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)
	}

	return validateTransactions(block.Data.Data, v.verifyRequest)
}

func validateTransactions(blockData [][]byte, verifyReq requestVerifier) ([]types.RequestInfo, error) {
	var validationFinished sync.WaitGroup
	validationFinished.Add(len(blockData))

	type txnValidation struct {
		indexInBlock  int
		extractedInfo types.RequestInfo
		validationErr error
	}

	noConfigAllowed := len(blockData) > 1

	validations := make(chan txnValidation, len(blockData))

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Resync/replay the ledger on the node so its last-config index matches the chain, then restart.
  2. Force a view change to get a proposal regenerated from consistent state.
  3. Check for ledger corruption (compare block hashes/numbers across orderers) and restore from a snapshot if the block's LAST_CONFIG entry is damaged.
  4. Ensure all consenters processed the same configuration transactions in the same order before continuing.

Example fix

// before: block metadata LAST_CONFIG disagrees with ledger
lastConf.Index = 90; lastConfig = 100 -> error
// after: rebuild/resync so the block's LAST_CONFIG points at the real config block
lastConf.Index = 100; lastConfig = 100
Defensive patterns

Strategy: validation

Validate before calling

lc := &common.LastConfig{}
if err := proto.Unmarshal(rawLastConfig.Value, lc); err != nil { return err }
if lc.Index != expectedLastConfig {
    return fmt.Errorf("block LAST_CONFIG %d != expected %d", lc.Index, expectedLastConfig)
}

Try / catch

if err := verifyProposal(prop); err != nil {
    if strings.Contains(err.Error(), "last config in block metadata") {
        // resync ledger / force view change
    }
    return err
}

Prevention

When it happens

Trigger: VerifyProposal -> verifyBlockDataAndMetadata: after unmarshalling cb.LastConfig from the block's BlockMetadataIndex_LAST_CONFIG entry, lastConf.Index != lastConfig. Occurs when the block's LAST_CONFIG metadata slice was built against a different last-config block number than the one the verifier computed.

Common situations: Ledger divergence between proposer and verifier; a block crafted by a node that computed last config from stale state; manual block edits or corrupted metadata; replayed blocks after a channel reconfiguration.

Related errors


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