diem/diem · error
Previous epoch ending LedgerInfo doesn't end an epoch
Error message
Previous epoch ending LedgerInfo doesn't end an epoch
What it means
When the waypoint formed by the backup's first LedgerInfo is not in the trusted waypoints set, the restore tries to cryptographically verify it against the previous LedgerInfo's `next_epoch_state`. That call requires the previous LedgerInfo to actually end an epoch (carry a next-epoch state); if it doesn't, this error is thrown. It means the supplied `previous_epoch_ending_ledger_info` is not an epoch-ending ledger info and cannot serve as a verification anchor.
Source
Thrown at storage/backup/backup-cli/src/backup_types/epoch_ending/restore.rs:236
if let Some(li) = previous_epoch_ending_ledger_info {
ensure!(
li.next_block_epoch() == preheat_data.manifest.first_epoch,
"Previous epoch ending LedgerInfo is not the one expected. \
My first epoch: {}, previous LedgerInfo next_block_epoch: {}",
preheat_data.manifest.first_epoch,
li.next_block_epoch(),
);
// Waypoint has been verified in preheat if it's trusted, otherwise try to check
// the signatures.
if self
.controller
.trusted_waypoints
.get(&first_li.ledger_info().version())
.is_none()
{
li.next_epoch_state()
.ok_or_else(|| {
anyhow!("Previous epoch ending LedgerInfo doesn't end an epoch")
})?
.verify(first_li)?;
}
}
let last_li = preheat_data
.ledger_infos
.last()
.expect("Verified not empty.")
.ledger_info();
match self.controller.run_mode.as_ref() {
RestoreRunMode::Restore { restore_handler } => {
restore_handler.save_ledger_infos(&preheat_data.ledger_infos)?;
EPOCH_ENDING_EPOCH.set(last_li.epoch() as i64);
EPOCH_ENDING_VERSION.set(last_li.version() as i64);
}
RestoreRunMode::Verify => {View on GitHub (pinned to fc4714a8ea)
Solutions
- Ensure `previous_epoch_ending_ledger_info` comes from the end of an epoch-ending backup (or a trusted waypoint), not a live node's current ledger info.
- Alternatively add the backup's first LedgerInfo version to the `trusted_waypoints` map so signature verification (and thus the next_epoch_state requirement) is skipped.
- Restore the missing preceding epoch-ending chunk so you have a genuine epoch-ending LedgerInfo to pass.
- Verify the checkpoint you load the previous LI from actually corresponds to an epoch boundary (check `next_epoch_state` is Some before calling run).
Example fix
// before: passing a mid-epoch ledger info as the anchor
let prev = client.get_ledger_info().await?;
restore.run(Some(&prev)).await?;
// after: require an epoch-ending ledger info or use None
let prev = client.get_ledger_info().await?;
let anchor = if prev.next_epoch_state().is_some() { Some(&prev) } else { None };
restore.run(anchor).await?; Defensive patterns
Strategy: validation
Validate before calling
// before calling run, ensure the anchor is an epoch-ending ledger info
// or register the backup waypoint as trusted
if let Some(li) = &prev_li {
ensure!(li.next_epoch_state().is_some(), "previous LI must end an epoch");
}
// alternatively: trusted_waypoints.insert(first_li_version, first_li.clone()); Type guard
fn is_epoch_ending(li: &LedgerInfo) -> bool {
li.next_epoch_state().is_some()
} Try / catch
match restore.run(Some(&prev_li)).await {
Err(e) if e.to_string().contains("doesn't end an epoch") => {
// switch to a genuine epoch-ending anchor or a trusted waypoint
Err(anyhow!("invalid anchor: {}", e))
}
other => other,
} Prevention
- Only pass LedgerInfos taken from epoch-ending backups (or trusted waypoints) as the previous ledger info.
- If trusting the backup's first LI, register its version in `trusted_waypoints` to skip signature verification.
- Never use a live node's current ledger info as an epoch-ending anchor.
- When in doubt, call `run(None)` (fresh restore from epoch 0) instead of an incremental restore.
When it happens
Trigger: `run` called with a `previous_epoch_ending_ledger_info` whose `next_epoch_state()` returns None (it's a mid-epoch LedgerInfo, not an epoch change proof), and the first backup LedgerInfo's version is not in `trusted_waypoints`.
Common situations: Passing a regular (non-epoch-ending) LedgerInfo fetched from a running node as the previous ledger info; the previous restore checkpoint was truncated mid-epoch; confusing ledger infos from `get_ledger_info` with epoch-ending ones.
Related errors
- Previous epoch ending LedgerInfo is not the one expected. My
- HOC has lower round than HLI
- No next_epoch_state specified in the provided Ledger Info
- Invalid Ordered LedgerInfoWithSignatures: Empty or at least
- No waypoint in config
AI-assisted analysis of diem/diem@fc4714a8ea (2026-09-04).
Data as JSON: /api/errors/ce792a868f3e65ab.
Report an issue: GitHub.