{"record":{"id":"5b9e862d9f806d69","repo":"nautechsystems/nautilus_trader","slug":"active-terminal-intent-conflicts-with-the-canonica","errorCode":null,"errorMessage":"Active terminal intent conflicts with the canonical nonce ledger","messagePattern":"Active terminal intent conflicts with the canonical nonce ledger","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":5467,"sourceCode":"                });\n                continue;\n            }\n\n            let nonce = intent.nonce.ok_or_else(|| {\n                anyhow::anyhow!(\"Retained signed intent {} has no nonce\", intent.id)\n            })?;\n            let current = current.ok_or_else(|| {\n                anyhow::anyhow!(\"Retained signed intent {} has no current hash\", intent.id)\n            })?;\n            let raw_transaction = authenticated.get(&current.id).ok_or_else(|| {\n                anyhow::anyhow!(\n                    \"Retained signed intent {} has no authenticated current payload\",\n                    intent.id\n                )\n            })?;\n\n            if intent.active && nonce == next_canonical_nonce {\n                anyhow::ensure!(\n                    !matches!(intent.status.as_str(), \"finalized\" | \"reverted\"),\n                    \"Active terminal intent conflicts with the canonical nonce ledger\"\n                );\n                records.push(ExecutionVerificationMigrationRecord {\n                    intent_id: intent.id,\n                    nonce: Some(nonce),\n                    transaction_hash: Some(current.transaction_hash.clone()),\n                    terminal_status: None,\n                    block_number: None,\n                    block_hash: None,\n                    receipt_success: None,\n                    gas_used: None,\n                    effective_gas_price: None,\n                    recover_prepared: false,\n                    decisions: vec![base_decision],\n                });\n                continue;\n            }","sourceCodeStart":5449,"sourceCodeEnd":5485,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L5449-L5485","documentation":"This invariant fires during migration when an intent is simultaneously marked `active` (still occupying its nonce in the signer's ledger) and carries a terminal status (`finalized` or `reverted`), while its nonce equals `next_canonical_nonce` — the nonce the ledger is about to hand out. A finalized/reverted transaction has been mined and its nonce consumed, so it cannot still be the active occupant of the next canonical nonce. The library throws to prevent the nonce ledger from double-tracking a consumed nonce.","triggerScenarios":"Raised by `anyhow::ensure!(!matches!(intent.status.as_str(), \"finalized\" | \"reverted\"), ...)` when a retained intent's status persisted as finalized/reverted but its `active` flag was not cleared, and its nonce matches the next canonical nonce — e.g. a crash between marking the intent terminal and deactivating it, or inconsistent updates that wrote status without flipping active.","commonSituations":"Node crash mid-terminal-update; a state-store write that is not atomic across the status and active fields; manual state edits that set status without clearing active; replaying old WAL entries in the wrong order after a restore.","solutions":["For the reported intent id, reconcile its `active` flag with its status: a finalized/reverted intent must have active=false in the state store, then restart migration.","Restore from the last consistent state snapshot taken before the interrupted terminal update.","Verify the transaction's actual on-chain state via its receipt and set the intent record to match reality (terminal, inactive) before re-running.","Make terminal status updates write `status` and `active` atomically in a single transaction to prevent recurrence."],"exampleFix":"// before (two separate writes; crash between them)\nstore.set_status(intent_id, \"finalized\");\nstore.set_active(intent_id, false);  // never ran after crash\n\n// after (atomic update)\nstore.update(intent_id, |i| { i.status = \"finalized\"; i.active = false; })?;","handlingStrategy":"validation","validationCode":"fn validate_intent_consistency(intent: &Intent) -> Result<(), String> {\n    let terminal = matches!(intent.status.as_str(), \"finalized\" | \"reverted\");\n    if terminal && intent.active {\n        return Err(format!(\"intent {} terminal but still active\", intent.id));\n    }\n    Ok(())\n}","typeGuard":"fn is_consistent(intent: &Intent) -> bool {\n    !matches!(intent.status.as_str(), \"finalized\" | \"reverted\") || !intent.active\n}","tryCatchPattern":null,"preventionTips":["Update status and active flag atomically in a single persistence transaction.","Enable journaling/WAL on the state store to survive crashes mid-update.","After any crash, run a reconciliation pass clearing active on terminal intents before migration.","Avoid manual edits to status fields in the state store."],"tags":["blockchain","nonce-ledger","inconsistent-state","invariant"],"backgroundTag":"invalid-state-transition","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"}