nautechsystems/nautilus_trader · error · anyhow::Error
Verified finality manifest identity changed
Error message
Verified finality manifest identity changed
What it means
Thrown by `record_execution_finality_verified` after locking the canonical nonce ledger row. The function compares the stored (manifest_version, manifest_digest) in the execution_verification_nonce table against the manifest identity carried by the incoming ExecutionFinalityTransition; if either differs, this ensure! fires. It is an integrity guard ensuring finality evidence is only persisted against the same verified manifest the ledger was created with.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:6948
self.pool.begin().await.map_err(|e| {
anyhow::anyhow!("Failed to start verified finality transition: {e}")
})?;
let (manifest_version, manifest_digest, stored_nonce, revision) =
sqlx::query_as::<_, (String, String, i64, i64)>(
"
SELECT manifest_version, manifest_digest, next_canonical_nonce, revision
FROM execution_verification_nonce
WHERE chain_id = $1 AND wallet_address = $2
FOR UPDATE
",
)
.bind(chain_id)
.bind(finality.wallet_address)
.fetch_optional(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to lock finality nonce ledger: {e}"))?
.ok_or_else(|| anyhow::anyhow!("Canonical nonce ledger is not initialized"))?;
anyhow::ensure!(
manifest_version == finality.manifest_version
&& manifest_digest == finality.manifest_digest,
"Verified finality manifest identity changed"
);
anyhow::ensure!(
stored_nonce == nonce,
"Finalized nonce {} does not match canonical nonce {stored_nonce}",
finality.nonce
);
let stored_tip = 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
",
)View on GitHub (pinned to 18893faf8b)
Solutions
- Re-verify the execution manifest so the ledger's manifest_version and manifest_digest are updated to the current identity before recording finality
- Ensure the exact same manifest bytes that produced the stored digest are being verified; recompute the digest and compare before calling record_execution_finality_verified
- If the manifest intentionally changed, re-initialize/rebuild the verification state for this chain/wallet from scratch under the new manifest
- Check for a library version mismatch: pin all processes writing to the database to the same adapter version and manifest format
Example fix
// before: finality transition built from a stale/edited manifest let finality = build_finality(&edited_manifest, ...); cache.record_execution_finality_verified(&finality).await?; // digest mismatch // after: assert identity matches before persisting assert_eq!(manifest.digest().to_string(), stored_digest); let finality = build_finality(&verified_manifest, ...); cache.record_execution_finality_verified(&finality).await?;
Defensive patterns
Strategy: validation
Validate before calling
let (version, digest): (String, String) = sqlx::query_as(
"SELECT manifest_version, manifest_digest FROM execution_verification_nonce WHERE chain_id = $1 AND wallet_address = $2"
)
.bind(chain_id).bind(wallet_address).fetch_one(&pool).await?;
anyhow::ensure!(
version == manifest.version && digest == manifest.digest().to_string(),
"Manifest identity drifted; re-verify before recording finality"
); Try / catch
match cache.record_execution_finality_verified(&finality).await {
Err(e) if e.to_string().contains("manifest identity changed") => {
// halt and re-verify the manifest; do not blindly retry
}
result => result?,
} Prevention
- Treat the manifest digest as immutable content addressing: any manifest edit requires a full re-verification pass
- Pin the adapter and manifest format version across all writers sharing one database
- Log and compare manifest digests at startup to detect drift before any finality recording
When it happens
Trigger: Calling record_execution_finality_verified when finality.manifest_version or finality.manifest_digest differs from the values stored for that (chain_id, wallet_address). Typical: the execution manifest was regenerated or edited after the ledger was created, a different build/version of the manifest was verified, or the digest computation changed between library versions.
Common situations: Upgrading the adapter/manifest format while reusing an old database; editing the execution manifest (changing intents or ordering) so its digest no longer matches; running two processes with different manifest versions against the same database; replaying historical finality data recorded under an older manifest digest.
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
- Finalized nonce {} does not match canonical nonce {stored_no
- Verified finalized header tip does not match the ledger
- Execution schema version {} is newer than supported version
- Verified finalized transaction count advanced without an act
- Failed to load active execution intent: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f21f2385dd66bb99.
Report an issue: GitHub.