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

  1. Stop the node and verify DB integrity; the epoch-ending ledger info column family is corrupt or incomplete.
  2. Restore the storage directory from a full, verified snapshot/backup that includes epoch-ending ledger infos.
  3. Re-sync the node from scratch from genesis (or latest trusted waypoint) if no good backup exists.
  4. 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

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


AI-assisted analysis of diem/diem@fc4714a8ea (2026-09-04). Data as JSON: /api/errors/ee52a6c5795dab7c. Report an issue: GitHub.