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(¤t_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
- Read the interpolated `{e}` to get the underlying sqlx/Postgres error
- If it is a lock timeout, reduce contention: shorten surrounding transactions or retry the finality application with backoff
- Verify the DB role has SELECT (and FOR UPDATE) privileges on the intent table
- 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
- Keep the intent-lock transaction short to reduce lock contention
- Serialize finality application per intent (single worker or advisory lock)
- Grant the cache DB role SELECT/UPDATE on intent tables before rollout
- Set explicit, generous statement_timeout for finality transactions
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
- Failed to lock execution verification state: {e}
- Failed to lock execution intent {intent_id}: {e}
- Implement FromRow for FuturesSpread
- Implement FromRow for OptionSpread
- Failed to load account events: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/c31ef6836cf48143.
Report an issue: GitHub.