nautechsystems/nautilus_trader · error · anyhow::Error

Failed to lock execution intent

Error message

Failed to lock execution intent {intent_id}: {e}

What it means

The FOR UPDATE row lock on the execution_intent row failed at the database level. The library wraps the sqlx error so the caller knows it could not acquire a lock on the intent row, which is required to safely advance the intent's status during signing persistence.

Solutions

  1. Retry the operation after a short backoff — the conflicting transaction usually commits or rolls back quickly.
  2. Check pg_locks / pg_stat_activity for the transaction blocking the execution_intent row and resolve the long-running holder.
  3. Increase statement/lock timeout if contention is expected, or shorten surrounding transactions.
  4. Verify database connectivity and re-establish the pool if the error indicates connection loss.
Defensive patterns

Strategy: retry

Validate before calling

// pre-check contention
let blocked = sqlx::query_scalar::<_, i64>("SELECT count(*) FROM pg_locks WHERE relation = 'execution_intent'::regclass AND NOT granted").fetch_one(&pool).await?;
if blocked > 0 { tokio::time::sleep(Duration::from_millis(200)).await; }

Try / catch

let row = retry(3, backoff(100).factor(2.0), || async {
    db.add_execution_transaction(intent_id, chain_id, hash, sealed).await
}).await.context("failed to lock execution intent after retries")?;

Prevention

When it happens

Trigger: The SELECT ... FOR UPDATE on execution_intent WHERE id = $1 returned a database error: connection loss mid-query, statement timeout while another transaction holds the row lock, serialization/deadlock abort, or the row being blocked by a long-running transaction.

Common situations: Two workers persisting signed transactions for the same intent concurrently with long outer transactions; a stuck transaction holding the lock until lock_timeout fires; network blip to PostgreSQL during the statement.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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

Appendix: source

Thrown at crates/adapters/blockchain/src/cache/database.rs:6658

            let marker = sqlx::query_scalar::<_, bool>(
                "SELECT EXISTS (SELECT 1 FROM execution_schema_version WHERE component = $1)",
            )
            .bind(EXECUTION_PAYLOAD_COMPONENT)
            .fetch_one(&mut *transaction)
            .await
            .context("failed to inspect execution payload marker")?;
            anyhow::ensure!(
                !marker,
                "Plaintext signed transaction persistence is disabled after payload activation"
            );
        }
        let current_status = sqlx::query_scalar::<_, String>(
            "SELECT status FROM execution_intent WHERE id = $1 FOR UPDATE",
        )
        .bind(intent_id)
        .fetch_optional(&mut *transaction)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to lock execution intent {intent_id}: {e}"))?
        .ok_or_else(|| anyhow::anyhow!("Execution intent {intent_id} was not found"))?;
        anyhow::ensure!(
            current_status == TransactionStatus::Prepared.as_str()
                || current_status == TransactionStatus::Signed.as_str(),
            "Execution intent {intent_id} is {current_status}, not prepared for signing"
        );

        let row = sqlx::query_as::<_, ExecutionTransactionHashRow>(
            "
            INSERT INTO execution_transaction_hash (
                intent_id, chain_id, transaction_hash, payload_expected,
                raw_transaction, sealed_transaction, status
            )
            SELECT id, chain_id, $3, TRUE, $4, $5, 'signed'
            FROM execution_intent
            WHERE id = $1 AND chain_id = $2 AND nonce IS NOT NULL
            ON CONFLICT (chain_id, transaction_hash) DO UPDATE
            SET transaction_hash = EXCLUDED.transaction_hash

View on GitHub (pinned to 18893faf8b)