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

  1. Re-verify the execution manifest so the ledger's manifest_version and manifest_digest are updated to the current identity before recording finality
  2. 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
  3. If the manifest intentionally changed, re-initialize/rebuild the verification state for this chain/wallet from scratch under the new manifest
  4. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/f21f2385dd66bb99. Report an issue: GitHub.