{"record":{"id":"57b5ca2750f7a152","repo":"nautechsystems/nautilus_trader","slug":"verified-nonce-assignment-manifest-identity-change","errorCode":null,"errorMessage":"Verified nonce assignment manifest identity changed","messagePattern":"Verified nonce assignment manifest identity changed","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":5928,"sourceCode":"            .begin()\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Failed to start verified nonce assignment: {e}\"))?;\n        let (manifest_version, manifest_digest, next_nonce, revision) =\n            sqlx::query_as::<_, (String, String, i64, i64)>(\n                \"\n                SELECT manifest_version, manifest_digest, next_canonical_nonce, revision\n                FROM execution_verification_nonce\n                WHERE chain_id = $1 AND wallet_address = $2\n                FOR UPDATE\n                \",\n            )\n            .bind(chain_id)\n            .bind(assignment.wallet_address)\n            .fetch_optional(&mut *transaction)\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Failed to lock canonical nonce ledger: {e}\"))?\n            .ok_or_else(|| anyhow::anyhow!(\"Canonical nonce ledger is not initialized\"))?;\n        anyhow::ensure!(\n            manifest_version == assignment.manifest_version\n                && manifest_digest == assignment.manifest_digest,\n            \"Verified nonce assignment manifest identity changed\"\n        );\n        anyhow::ensure!(\n            next_nonce == nonce,\n            \"Execution nonce {} does not match canonical nonce {next_nonce}\",\n            assignment.nonce\n        );\n\n        let (intent_chain_id, intent_wallet, intent_nonce, intent_status, intent_active) =\n            sqlx::query_as::<_, (i32, String, Option<i64>, String, bool)>(\n                \"\n            SELECT chain_id, wallet_address, nonce, status, active\n            FROM execution_intent\n            WHERE id = $1\n            FOR UPDATE\n            \",","sourceCodeStart":5910,"sourceCodeEnd":5946,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L5910-L5946","documentation":"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!`.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n // manifest bumped but ledger still on old digest\n let assignment = Assignment::build(new_manifest, ...)?;\n// after\n db.reinitialize_nonce_ledger(chain_id, wallet, &new_manifest).await?; // rebind identity first\n let assignment = Assignment::build(new_manifest, ...)?;","handlingStrategy":"validation","validationCode":"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?;\nmatch stored {\n    Some((v, d)) if v == assignment.manifest_version && d == assignment.manifest_digest => Ok(()),\n    _ => Err(anyhow!(\"manifest identity drift; reinitialize ledger before assigning\")),\n}","typeGuard":"fn manifest_matches(stored: &ManifestIdentity, assignment: &Assignment) -> bool {\n    stored.version == assignment.manifest_version && stored.digest == assignment.manifest_digest\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"manifest identity changed\") => {\n        // halt assignments; require operator-approved re-initialization\n        halt_and_reinit_ledger(manifest)?\n    }\n    other => other,\n}","preventionTips":["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."],"tags":["consistency","manifest","state-mismatch","database"],"backgroundTag":"checksum-mismatch","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}