diem/diem · critical
DB corruption: missing epoch ending ledger info for epoch {}
Error message
DB corruption: missing epoch ending ledger info for epoch {} What it means
DiemDB verifies that the epoch-ending ledger info iterator returns one entry per epoch in the requested range. If fewer entries are returned than epochs requested, the persistent ledger store is missing epoch-ending records, indicating on-disk data corruption or an incomplete backup/restore. The library throws this instead of returning a partial or inconsistent epoch list to callers.
Source
Thrown at storage/diemdb/src/lib.rs:391
.next_block_epoch();
ensure!(
end_epoch <= latest_epoch,
"Unable to provide epoch change ledger info for still open epoch. asked upper bound: {}, last sealed epoch: {}",
end_epoch,
latest_epoch - 1, // okay to -1 because genesis LedgerInfo has .next_block_epoch() == 1
);
let (paging_epoch, more) = if end_epoch - start_epoch > limit as u64 {
(start_epoch + limit as u64, true)
} else {
(end_epoch, false)
};
let lis = self
.ledger_store
.get_epoch_ending_ledger_info_iter(start_epoch, paging_epoch)?
.collect::<Result<Vec<_>>>()?;
ensure!(
lis.len() == (paging_epoch - start_epoch) as usize,
"DB corruption: missing epoch ending ledger info for epoch {}",
lis.last()
.map(|li| li.ledger_info().next_block_epoch())
.unwrap_or(start_epoch),
);
Ok((lis, more))
}
fn get_transaction_with_proof(
&self,
version: Version,
ledger_version: Version,
fetch_events: bool,
) -> Result<TransactionWithProof> {
let proof = self
.ledger_store
.get_transaction_info_with_proof(version, ledger_version)?;View on GitHub (pinned to fc4714a8ea)
Solutions
- Stop the node and verify DB integrity; the epoch-ending ledger info column family is corrupt or incomplete.
- Restore the storage directory from a full, verified snapshot/backup that includes epoch-ending ledger infos.
- Re-sync the node from scratch from genesis (or latest trusted waypoint) if no good backup exists.
- Check disk health (filesystem errors, RocksDB corruption logs) and run db-tool/backup tooling to validate the backup before re-serving queries.
Defensive patterns
Strategy: validation
Validate before calling
// before calling get_epoch_ending_ledger_infos
let start = start_epoch;
let end = start_epoch + num_epochs;
let infos = db.get_epoch_ending_ledger_infos(start, end)?;
if (infos.len() as u64) != num_epochs {
return Err("epoch-ending ledger info range incomplete; DB likely corrupted");
} Try / catch
match db.get_epoch_ending_ledger_infos(start, end) {
Ok(infos) if infos.len() == expected => Ok(infos),
Ok(_) | Err(e @ Error::UnexpectedError(..)) => restore_from_backup_or_resync(),
} Prevention
- Always restore nodes from verified full backups (db-backup tooling), never partial column-family copies.
- Monitor disk health and RocksDB corruption logs.
- Run DB integrity verification after any restore or storage upgrade before serving traffic.
- Keep the node's storage format/migration fully completed before query workloads.
When it happens
Trigger: Calling get_epoch_ending_ledger_infos_impl (via verify_epochs or get_epoch_ending_ledger_infos) when the ledger_store iterator over [start_epoch, paging_epoch) returns a Vec shorter than paging_epoch - start_epoch, i.e. one or more epochs have no ending ledger info row.
Common situations: Restoring a node from a partial or pruned backup that skipped epoch-ending ledger infos; RocksDB data files corrupted or truncated; running state sync against a DB written by an older/incompatible format; manually copying a subset of DB column families.
Related errors
- DB corrupt: Sequence number not continuous, expected: {}, ac
- DB corruption: looking for epoch for version {}, got epoch {
- Epoch {} didn't end at version {}
- Epochs are not consecutive.
- Index broken, expected seq:{}, actual:{}
AI-assisted analysis of diem/diem@fc4714a8ea (2026-09-04).
Data as JSON: /api/errors/ee52a6c5795dab7c.
Report an issue: GitHub.