nautechsystems/nautilus_trader · error · anyhow::Error
Verified nonce assignment manifest identity changed
Error message
Verified nonce assignment manifest identity changed
What it means
After locking the ledger row, the code asserts that the stored manifest_version and manifest_digest match those carried by the incoming assignment. A mismatch means the execution manifest changed underneath the canonical nonce ledger — the ledger's identity is bound to a specific manifest, and assigning nonces against a different manifest would be unsafe. The transaction is aborted via `anyhow::ensure!`.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:5928
.begin()
.await
.map_err(|e| anyhow::anyhow!("Failed to start verified nonce assignment: {e}"))?;
let (manifest_version, manifest_digest, next_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(assignment.wallet_address)
.fetch_optional(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to lock canonical nonce ledger: {e}"))?
.ok_or_else(|| anyhow::anyhow!("Canonical nonce ledger is not initialized"))?;
anyhow::ensure!(
manifest_version == assignment.manifest_version
&& manifest_digest == assignment.manifest_digest,
"Verified nonce assignment manifest identity changed"
);
anyhow::ensure!(
next_nonce == nonce,
"Execution nonce {} does not match canonical nonce {next_nonce}",
assignment.nonce
);
let (intent_chain_id, intent_wallet, intent_nonce, intent_status, intent_active) =
sqlx::query_as::<_, (i32, String, Option<i64>, String, bool)>(
"
SELECT chain_id, wallet_address, nonce, status, active
FROM execution_intent
WHERE id = $1
FOR UPDATE
",View on GitHub (pinned to 18893faf8b)
Solutions
- Re-initialize or migrate the nonce ledger to the new manifest identity through the sanctioned bootstrap path (do not hand-edit rows).
- Ensure all replicas run the same manifest version before processing assignments.
- Purge stale queued assignments produced under the old manifest digest.
- Compare the DB row's manifest_version/manifest_digest against the assignment to confirm which side drifted.
- If the mismatch is unintentional, fix the runtime configuration so the manifest matches the ledger's recorded identity.
Example fix
// before // manifest bumped but ledger still on old digest let assignment = Assignment::build(new_manifest, ...)?; // after db.reinitialize_nonce_ledger(chain_id, wallet, &new_manifest).await?; // rebind identity first let assignment = Assignment::build(new_manifest, ...)?;
Defensive patterns
Strategy: validation
Validate before calling
let stored: Option<(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).fetch_optional(pool).await?;
match stored {
Some((v, d)) if v == assignment.manifest_version && d == assignment.manifest_digest => Ok(()),
_ => Err(anyhow!("manifest identity drift; reinitialize ledger before assigning")),
} Type guard
fn manifest_matches(stored: &ManifestIdentity, assignment: &Assignment) -> bool {
stored.version == assignment.manifest_version && stored.digest == assignment.manifest_digest
} Try / catch
match result {
Err(e) if e.to_string().contains("manifest identity changed") => {
// halt assignments; require operator-approved re-initialization
halt_and_reinit_ledger(manifest)?
}
other => other,
} Prevention
- Treat manifest updates as migrations: bump version and rebind the ledger in one coordinated step.
- Deploy all replicas with the same manifest version; avoid mixed-version rolling writes.
- Purge or version-stamp queued assignments when the manifest changes.
- Log manifest_version/manifest_digest at startup and diff against the DB row.
- Never hand-edit manifest columns in the database.
When it happens
Trigger: An assignment built from a different manifest_version or manifest_digest than the one recorded when the ledger row was initialized — e.g. the service was redeployed with an updated execution manifest while the database still holds the old manifest identity, or stale in-process state from before a manifest reload is persisted.
Common situations: Rolling deployment where one replica runs the old manifest and writes assignments while the DB reflects the new one; manifest edited without re-initializing the nonce ledger; replaying old queued assignments after a manifest bump; copying a database between environments with different manifests.
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
- Verified finalized transaction count advanced without an act
- Account event does not exist for account: {}
- Execution verification manifest identity changed
- Finalized header manifest identity changed
- Verified finalized transaction count is outside the owned re
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/57b5ca2750f7a152.
Report an issue: GitHub.