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

  1. 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.
  2. Check the orderer logs for a prior failure committing the join block, and ensure the ledger path is writable and not mounted empty.
  3. If the ledger was intentionally reset, follow the documented reset/rollback procedure (orderer reset) so the genesis block is restored.
  4. 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

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


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