{"record":{"id":"92596a6c385181b7","repo":"nautechsystems/nautilus_trader","slug":"failed-to-lock-execution-intent-for-nonce-assignme","errorCode":null,"errorMessage":"Failed to lock execution intent for nonce assignment: {e}","messagePattern":"Failed to lock execution intent for nonce assignment: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":5952,"sourceCode":"            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            \",\n            )\n            .bind(assignment.intent_id)\n            .fetch_optional(&mut *transaction)\n            .await\n            .map_err(|e| {\n                anyhow::anyhow!(\"Failed to lock execution intent for nonce assignment: {e}\")\n            })?\n            .ok_or_else(|| {\n                anyhow::anyhow!(\"Execution intent {} was not found\", assignment.intent_id)\n            })?;\n        anyhow::ensure!(\n            intent_chain_id == chain_id\n                && intent_wallet == assignment.wallet_address\n                && intent_status == \"prepared\"\n                && intent_active\n                && intent_nonce.is_none_or(|assigned| assigned == nonce),\n            \"Execution intent {} cannot own canonical nonce {}\",\n            assignment.intent_id,\n            assignment.nonce\n        );\n\n        for (index, decision) in assignment.decisions.iter().enumerate() {\n            let height_start = decision\n                .height_start","sourceCodeStart":5934,"sourceCodeEnd":5970,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L5934-L5970","documentation":"In `assign_execution_intent_nonce_verified`, the code issues `SELECT ... FROM execution_intent WHERE id = $1 FOR UPDATE` inside a transaction to lock the intent row for a canonical nonce assignment. This error wraps any SQLx failure of that locking SELECT (connection loss, deadlock/lock timeout, syntax/schema mismatch, cancellation). The library throws it because nonce assignment must serialize on the intent row; if the lock query itself fails, the whole atomic operation is aborted and the caller gets this anyhow-wrapped error.","triggerScenarios":"Calling `assign_execution_intent_nonce_verified` when: the Postgres connection drops mid-transaction (pool error, TLS failure); the SELECT ... FOR UPDATE deadlocks or exceeds lock_timeout because another transaction holds a conflicting lock on the same execution_intent row; the execution_intent table/columns are missing (unmigrated database); or the query is cancelled.","commonSituations":"Two workers assigning nonces for the same wallet concurrently causing lock contention or deadlock; database restart or failover during the call; running against a database without the execution_intent schema applied; long-running transactions hitting idle-in-transaction timeouts.","solutions":["Check database connectivity and pool health (connection limits, TLS, idle timeouts) and retry the operation; the transaction was rolled back so it is safe to re-run.","Verify migrations have been applied so the execution_intent table and columns exist.","Reduce lock contention: keep the pre-lock work short, avoid other transactions that lock execution_intent rows in a different order, and tune deadlock_timeout/lock_timeout.","Inspect the inner `{e}` SQLx error (it is preserved in the message) for the concrete cause, e.g. `40P01 deadlock_detected` or `57P01 admin shutdown`."],"exampleFix":"// before: firing many assignments concurrently for the same intent/wallet\nlet handles: Vec<_> = intents.iter().map(|a| db.assign_execution_intent_nonce_verified(a)).collect();\n// after: serialize assignments per (chain_id, wallet) or retry on transient DB errors\nfor a in intents {\n    match db.assign_execution_intent_nonce_verified(a).await {\n        Ok(()) => {}\n        Err(e) if is_transient_db_error(&e) => backoff_retry(|| db.assign_execution_intent_nonce_verified(a)).await?,\n        Err(e) => return Err(e),\n    }\n}","handlingStrategy":"retry","validationCode":"let exists: Option<i64> = sqlx::query_scalar(\"SELECT 1 FROM execution_intent WHERE id = $1\").bind(&assignment.intent_id).fetch_optional(pool).await?;\nanyhow::ensure!(exists.is_some(), \"intent {} missing before assignment\", assignment.intent_id);","typeGuard":null,"tryCatchPattern":"match db.assign_execution_intent_nonce_verified(&assignment).await {\n    Ok(()) => {}\n    Err(e) if e.to_string().contains(\"Failed to lock execution intent\") && is_transient(&e) => backoff_retry(...).await?,\n    Err(e) => return Err(e),\n}","preventionTips":["Apply all migrations before running assignment flows so execution_intent exists.","Keep transactions short and lock execution_intent rows in a consistent order to avoid deadlocks.","Set sane statement_timeout/lock_timeout and pool limits; monitor connection drops.","Only retry on transient DB errors (connection resets, deadlocks, serialization failures); never on schema errors."],"tags":["database","sqlx","postgres","row-locking","transaction"],"backgroundTag":"database-query-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}