hyperledger/fabric · critical
ledger is empty
Error message
ledger is empty
What it means
Chain.loadLastConfig reads the follower's ledger to locate the last configuration block, but Height() returned 0 — the ledger contains no blocks at all. The code fails fast because a follower chain cannot operate without at least the join/genesis block present.
Source
Thrown at orderer/common/follower/follower_chain.go:582
}
if updateEndpoints {
endpoints, err := cluster.EndpointconfigFromConfigBlock(nextBlock, c.cryptoProvider)
if err != nil {
return n, errors.WithMessagef(err, "failed to extract endpoints from last config, block number: %d", nextBlock.Header.Number)
}
c.blockPuller.UpdateEndpoints(endpoints)
}
}
}
}
c.logger.Debugf("Pulled blocks from %d to %d", firstBlockToPull, targetHeight)
return targetHeight - firstBlockToPull, nil
}
func (c *Chain) loadLastConfig() error {
height := c.ledgerResources.Height()
if height == 0 {
return errors.New("ledger is empty")
}
lastBlock := c.ledgerResources.Block(height - 1)
index, err := protoutil.GetLastConfigIndexFromBlock(lastBlock)
if err != nil {
return errors.WithMessage(err, "chain does have appropriately encoded last config in its latest block")
}
lastConfig := c.ledgerResources.Block(index)
if lastConfig == nil {
return errors.Errorf("could not retrieve config block from index %d", index)
}
c.lastConfig = lastConfig
return nil
}
View on GitHub (pinned to 2736b63f8f)
Solutions
- Re-join the channel so the join block is appended: POST to the channel Join API with a valid config block, or recreate the ledger genesis.
- Check the orderer logs for a prior failure committing the join block, and ensure the ledger path is writable and not mounted empty.
- If the ledger was intentionally reset, follow the documented reset/rollback procedure (orderer reset) so the genesis block is restored.
- Validate the onboarding pipeline (joinBlock retrieval) — a nil/failed joinBlock leaves height at 0.
Defensive patterns
Strategy: validation
Validate before calling
if ledgerResources.Height() == 0 { return errors.New("refusing to start follower: ledger has no blocks; join the channel first") } Try / catch
if err := NewChain(...); err != nil && err.Error() == "ledger is empty" { re-join channel to append join block, then retry } Prevention
- Ensure the channel Join API succeeded (check response + logs) before starting chain components
- Keep the join block artifact and re-apply it after any ledger reset
- Use the documented orderer reset/rollback procedures instead of manual file deletion
When it happens
Trigger: loadLastConfig is called from NewChain or pullAfterJoin when c.ledgerResources.Height() == 0, i.e. the ledger was created but never populated with the join block, or all blocks were removed before the chain started.
Common situations: Orderer joined a channel via the Join REST API but the join block failed to be committed; ledger directory wiped or recreated by admin/automation while state files persisted; a fresh container where the genesis/join block generation step failed.
Related errors
- cannot retrieve previous block %d
- cannot enable channel capabilities without orderer support f
- Must set some OrdererAddresses
- failed computing orderer addresses
- orderer is required, but no ordering endpoint or endorser cl
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/0ef6988061b413b6.
Report an issue: GitHub.