nautechsystems/nautilus_trader · error

Failed to lock intent for verified action: {e}

Error message

Failed to lock intent for verified action: {e}

What it means

Thrown when the SELECT that locks the active execution intent for a verified action (by intent_id, chain_id, wallet_address) fails with a database error. This is a query failure rather than a missing row; the transaction is aborted and no nonce or attempt state is read.

Source

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

            .ok_or_else(|| anyhow::anyhow!("Canonical nonce ledger is not initialized"))?;
        anyhow::ensure!(
            manifest_version == batch.manifest_version && manifest_digest == batch.manifest_digest,
            "Verified action manifest identity changed"
        );
        let intent_nonce = sqlx::query_scalar::<_, Option<i64>>(
            "
            SELECT nonce
            FROM execution_intent
            WHERE id = $1 AND chain_id = $2 AND wallet_address = $3 AND active
            FOR UPDATE
            ",
        )
        .bind(batch.intent_id)
        .bind(chain_id)
        .bind(batch.wallet_address)
        .fetch_optional(&mut *transaction)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to lock intent for verified action: {e}"))?
        .ok_or_else(|| anyhow::anyhow!("Active verified-action intent was not found"))?;
        anyhow::ensure!(
            intent_nonce == Some(nonce),
            "Verified action nonce does not match the active intent"
        );
        let attempt = sqlx::query_scalar::<_, i64>(
            "
            SELECT COUNT(*)
            FROM execution_verification_decision
            WHERE intent_id = $1 AND decision_class = $2
            ",
        )
        .bind(batch.intent_id)
        .bind(batch.decision_class)
        .fetch_one(&mut *transaction)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to number verified action evidence: {e}"))?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Retry the operation; the transaction rolls back cleanly and re-acquires the lock.
  2. Check Postgres logs for the underlying error (lock timeout, permission, connection drop).
  3. Reduce lock contention by shortening transactions that hold the intent row lock.
  4. Verify the database role can SELECT from the execution intent table.

Example fix

// before: no retry around lock acquisition
record_execution_verification_batch(&db, &batch).await?;
// after: retry on lock-related failures
match record_execution_verification_batch(&db, &batch).await {
    Err(e) if is_lock_timeout(&e) => { tokio::time::sleep(Duration::from_millis(200)).await; record_execution_verification_batch(&db, &batch).await?; }
    res => res?,
}
Defensive patterns

Strategy: retry

Try / catch

// Rust
match record_batch().await {
    Err(e) if e.to_string().contains("Failed to lock intent for verified action")
        && (is_lock_timeout(&e) || is_transient(&e)) => retry_with_backoff(record_batch),
    res => res,
}

Prevention

When it happens

Trigger: fetch_optional on the intent-locking SELECT returns Err — connection loss, statement timeout, permission error, or deadlock while the row lock is being acquired.

Common situations: Long-held locks by another transaction causing statement_timeout; database failover mid-query; missing SELECT grants on the intent table; proxy killing idle connections.

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


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