nautechsystems/nautilus_trader · error · anyhow::Error

Failed to lock intent for verified finality: {e}

Error message

Failed to lock intent for verified finality: {e}

What it means

This error wraps a sqlx failure on the `fetch_optional` SELECT that locks (SELECT ... FOR UPDATE semantics via the transaction) the active intent row before applying verified finality. It means the locking query itself errored — not that the intent is missing (that is error 2334). The transaction is aborted so no finality state changes are applied.

Source

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

                header.number
            );
        }

        let (current_status, intent_nonce, fill_emitted, terminal_emitted) =
            sqlx::query_as::<_, (String, Option<i64>, bool, bool)>(
                "
                SELECT status, nonce, fill_emitted, terminal_emitted
                FROM execution_intent
                WHERE id = $1 AND chain_id = $2 AND wallet_address = $3 AND active
                FOR UPDATE
                ",
            )
            .bind(finality.intent_id)
            .bind(chain_id)
            .bind(finality.wallet_address)
            .fetch_optional(&mut *transaction)
            .await
            .map_err(|e| anyhow::anyhow!("Failed to lock intent for verified finality: {e}"))?
            .ok_or_else(|| anyhow::anyhow!("Active finality intent was not found"))?;
        anyhow::ensure!(
            intent_nonce == Some(nonce)
                && execution_transition_allowed(&current_status, finality.status),
            "Intent cannot make the verified finality transition"
        );

        for (index, decision) in finality.decisions.iter().enumerate() {
            let height_start = decision
                .height_start
                .map(i64::try_from)
                .transpose()
                .context("Verification height exceeds PostgreSQL BIGINT")?;
            let height_end = decision
                .height_end
                .map(i64::try_from)
                .transpose()
                .context("Verification height exceeds PostgreSQL BIGINT")?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the interpolated `{e}` to get the underlying sqlx/Postgres error
  2. If it is a lock timeout, reduce contention: shorten surrounding transactions or retry the finality application with backoff
  3. Verify the DB role has SELECT (and FOR UPDATE) privileges on the intent table
  4. Check schema/columns used by the intent SELECT match current migrations
Defensive patterns

Strategy: retry

Validate before calling

// Verify table access and grants before finality runs
sqlx::query("SELECT 1 FROM execution_intent LIMIT 1")
    .fetch_optional(&mut *conn).await
    .map_err(|e| anyhow!("intent table unavailable: {e}"))?;

Try / catch

match result {
    Err(e) if is_lock_timeout(&e) || is_transient_db_error(&e) => {
        retry_with_backoff(3, || apply_verified_finality(...)).await
    }
    Err(e) => Err(e),
    Ok(v) => Ok(v),
}

Prevention

When it happens

Trigger: The intent-lock SELECT for (intent_id, chain_id, wallet_address) fails: connection loss, permission error on the table, schema mismatch, or a lock-wait/statement timeout while another transaction holds the intent row.

Common situations: Concurrent finality processors contending on the same intent row causing lock timeouts; the cache DB user lacking SELECT grants after a permissions change; long transactions blocking on the intent row until the statement timeout fires.

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/c31ef6836cf48143. Report an issue: GitHub.