nautechsystems/nautilus_trader · error · anyhow::Error
Verified finalized headers do not start at the trusted check
Error message
Verified finalized headers do not start at the trusted checkpoint
What it means
This error is raised when the verified-finalized header ledger is being initialized for the first time (no existing rows, `initialized == false`). The first header of the incoming verified batch must exactly equal the trusted bootstrap checkpoint (number, hash, parent_hash, timestamp, base_fee_per_gas); otherwise the entire ledger would be anchored to a header that is not the trusted checkpoint. The code `anyhow::ensure!`s this equality inside the transaction and aborts on mismatch, preventing the ledger from ever being seeded from an untrusted starting point.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:4448
.fetch_one(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to lock finalized header tip: {e}"))?;
anyhow::ensure!(
stored_tip
== (
i64::try_from(first_header.number)
.context("Verified finalized height exceeds PostgreSQL BIGINT")?,
first_header.hash.clone(),
first_header.parent_hash.clone(),
i64::try_from(first_header.timestamp)
.context("Verified finalized timestamp exceeds PostgreSQL BIGINT")?,
first_header.base_fee_per_gas.map(|value| value.to_string()),
bootstrap.manifest_digest.to_string(),
),
"Verified finalized header extension does not start at the durable tip"
);
} else {
anyhow::ensure!(
first_header.number == bootstrap.checkpoint_number
&& first_header.hash == bootstrap.checkpoint_hash
&& first_header.parent_hash == bootstrap.checkpoint_parent_hash
&& first_header.timestamp == bootstrap.checkpoint_timestamp
&& first_header.base_fee_per_gas == bootstrap.checkpoint_base_fee_per_gas,
"Verified finalized headers do not start at the trusted checkpoint"
);
}
for header in bootstrap.finalized_headers.iter().skip(1) {
let number = i64::try_from(header.number)
.context("Verified finalized height exceeds PostgreSQL BIGINT")?;
let timestamp = i64::try_from(header.timestamp)
.context("Verified finalized timestamp exceeds PostgreSQL BIGINT")?;
let base_fee = header.base_fee_per_gas.map(|value| value.to_string());
sqlx::query(
"
INSERT INTO execution_verified_finalized_header (View on GitHub (pinned to 18893faf8b)
Solutions
- Verify `bootstrap.checkpoint_number/hash/parent_hash/timestamp/base_fee_per_gas` against the first header of the verified batch and make the batch start exactly at the checkpoint height.
- Re-fetch checkpoint metadata from the trusted source (consensus checkpoint API or configured anchor) so it matches the headers the provider actually returns.
- Confirm chain_id and network configuration match between the checkpoint definition and the RPC/data provider.
- If the checkpoint itself is wrong/outdated, update the configured trusted checkpoint to one the verified header stream can begin from, then re-run initialization.
- Normalize base_fee representation (None vs zero) so checkpoint and header values compare equal.
Example fix
// before: checkpoint and batch start diverge
let checkpoint = Checkpoint { number: 1_000, hash: 0xa1.. };
let headers = fetch_verified_finalized(from: 1_050);
// after: seed the batch from the trusted checkpoint itself
let checkpoint = load_trusted_checkpoint(chain_id)?;
let headers = fetch_verified_finalized(from: checkpoint.number);
assert_eq!(headers[0].number, checkpoint.number);
assert_eq!(headers[0].hash, checkpoint.hash);
persist_finalized_headers(headers).await?; Defensive patterns
Strategy: validation
Validate before calling
fn matches_trusted_checkpoint(first: &VerifiedHeader, cp: &Checkpoint) -> bool {
first.number == cp.number
&& first.hash == cp.hash
&& first.parent_hash == cp.parent_hash
&& first.timestamp == cp.timestamp
&& first.base_fee_per_gas == cp.base_fee_per_gas
}
// call before bootstrapping an empty ledger
assert!(matches_trusted_checkpoint(&headers[0], &checkpoint), "batch must start at trusted checkpoint"); Type guard
fn starts_at_checkpoint(headers: &[VerifiedHeader], cp: &Checkpoint) -> bool {
headers.first().map(|h| h.number == cp.number && h.hash == cp.hash).unwrap_or(false)
} Try / catch
match bootstrap_verified_finalized(&pool, &bootstrap).await {
Err(e) if e.to_string().contains("do not start at the trusted checkpoint") => {
// reload checkpoint metadata or refetch headers from checkpoint height
}
Err(e) => return Err(e),
Ok(()) => {}
} Prevention
- Derive the verified batch start from the configured checkpoint, never from an arbitrary snapshot height.
- Validate checkpoint number/hash against the trusted source at startup.
- Ensure chain_id and network match between checkpoint config and data provider.
- Normalize base_fee (None vs zero) before comparing checkpoint and header values.
When it happens
Trigger: Calling the verified-finalized bootstrap against an empty ledger while the first entry of `bootstrap.finalized_headers` differs from `bootstrap.checkpoint_*` fields: starting the batch at a height above the checkpoint, a different hash/parent_hash at the checkpoint height, differing timestamp or base_fee_per_gas, or supplying checkpoint metadata from a different chain/manifest than the header batch.
Common situations: Misconfigured checkpoint values (stale or hand-edited checkpoint hash/number in config); checkpoint taken from a different network (e.g. sepolia vs mainnet) than the data source; provider returning verified headers starting after the checkpoint; timestamp/base-fee fields normalized differently between the checkpoint source and the header source (e.g. zero vs None base fee).
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Verified finalized header extension does not start at the du
- Finalized header ledger conflicts at height {}
- Replacement hash {transaction_hash} conflicts with another i
- Execution schema version {} is newer than supported version
- Verified finalized transaction count advanced without an act
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b4d8a3b4a9a4b4a7.
Report an issue: GitHub.