nautechsystems/nautilus_trader · critical · anyhow::Error
Finalized header manifest identity changed
Error message
Finalized header manifest identity changed
What it means
Raised in database.rs:3732 when the manifest_digest stored on the latest verified finalized header row does not equal the caller's manifest_digest. The tip header was recorded under a different verification manifest, so its data cannot be trusted as a continuation point for the current manifest's verification.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:3732
"Execution verification manifest identity changed"
);
let row = sqlx::query_as::<_, (i64, String, String, i64, Option<String>, String)>(
"
SELECT number, hash, parent_hash, timestamp, base_fee_per_gas, manifest_digest
FROM execution_verified_finalized_header
WHERE chain_id = $1 AND wallet_address = $2
ORDER BY number DESC
LIMIT 1
",
)
.bind(chain_id)
.bind(wallet_address)
.fetch_optional(&self.pool)
.await
.context("failed to load verified finalized header tip")?
.ok_or_else(|| anyhow::anyhow!("Verified finalized header ledger is empty"))?;
let (number, hash, parent_hash, timestamp, base_fee, digest) = row;
anyhow::ensure!(
digest == manifest_digest,
"Finalized header manifest identity changed"
);
Ok(Some(ExecutionVerificationPosition {
next_canonical_nonce: u64::try_from(nonce).context("Canonical nonce is negative")?,
revision: u64::try_from(revision).context("Canonical nonce revision is negative")?,
finalized_tip: ExecutionVerifiedHeader {
number: u64::try_from(number).context("Finalized header number is negative")?,
hash,
parent_hash,
timestamp: u64::try_from(timestamp)
.context("Finalized header timestamp is negative")?,
base_fee_per_gas: base_fee
.map(|value| {
value
.parse::<u128>()
.map_err(|_| anyhow::anyhow!("Finalized header base fee is invalid"))
})View on GitHub (pinned to 18893faf8b)
Solutions
- Re-bootstrap verification state under the current manifest so headers are re-recorded with the active digest.
- Ensure the manifest_digest passed to load_execution_verification_position is identical to the one used when headers were written.
- Revert the manifest configuration change if it was unintended (compare digests against the stored rows).
- Do not delete or rewrite the digest column manually; rotate state through the bootstrap path instead.
Example fix
// before: digest mismatch on tip header let pos = db.load_execution_verification_position(chain, wallet, ver, &new_digest).await?; // Err: Finalized header manifest identity changed // after: use the digest under which the ledger was written, or re-bootstrap under new_digest let pos = db.load_execution_verification_position(chain, wallet, ver, &ledger_digest).await?;
Defensive patterns
Strategy: validation
Validate before calling
let tip: Option<String> = sqlx::query_scalar(
"SELECT manifest_digest FROM execution_verified_finalized_header \
WHERE chain_id = $1 AND wallet_address = $2 ORDER BY number DESC LIMIT 1",
).bind(chain_id).bind(wallet).fetch_optional(&pool).await?;
if tip.as_deref() != Some(manifest_digest) {
anyhow::bail!("ledger digest {:?} != active manifest digest", tip);
} Try / catch
match load_position(...).await {
Err(e) if e.to_string().contains("Finalized header manifest identity changed") => {
// stop; re-bootstrap or revert manifest config before continuing
},
other => other?,
} Prevention
- Keep manifest digest in config under change control
- Re-bootstrap whenever the manifest digest changes
- Diff active config digest against the stored ledger digest at startup
When it happens
Trigger: Loading the verified finalized header tip in load_execution_verification_position when the digest column of the newest execution_verified_finalized_header row differs from the manifest_digest parameter passed by the caller.
Common situations: Verification manifest changed (contract set or rules updated) after headers were already recorded; restoring a mixed database where headers came from a different deployment; passing the wrong manifest digest in code/config.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- Execution verification manifest identity changed
- Verified finalized header ledger is empty
- Verified nonce assignment manifest identity changed
- Signed transaction {transaction_hash} conflicts with its per
- Verified finalized header ledger mismatch for height {number
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/4cdf5f396f03f521.
Report an issue: GitHub.