nautechsystems/nautilus_trader · error · anyhow::Error
Failed to assign nonce {nonce} to execution intent: {e}
Error message
Failed to assign nonce {nonce} to execution intent: {e} What it means
Thrown when the guarded UPDATE in assign_execution_intent_nonce fails at the database level (the zero-rows case is a separate error). Because it is a single UPDATE executed on the pool, failures are connection loss, statement timeout, or row-lock contention with the FOR UPDATE readers in record_execution_status and add_execution_transaction_hash.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:3416
intent_id: i64,
nonce: u64,
) -> anyhow::Result<()> {
let nonce_db = i64::try_from(nonce)
.with_context(|| format!("Execution nonce {nonce} exceeds PostgreSQL BIGINT"))?;
let result = sqlx::query(
"
UPDATE execution_intent
SET nonce = $2, updated_at = NOW()
WHERE id = $1
AND status = 'prepared'
AND (nonce IS NULL OR nonce = $2)
",
)
.bind(intent_id)
.bind(nonce_db)
.execute(&self.pool)
.await
.map_err(|e| anyhow::anyhow!("Failed to assign nonce {nonce} to execution intent: {e}"))?;
anyhow::ensure!(
result.rows_affected() == 1,
"Execution intent {intent_id} is not prepared for nonce {nonce}"
);
Ok(())
}
/// Releases an intent when no broadcast attempt can have occurred.
///
/// # Errors
///
/// Returns an error if the intent advanced to broadcast or persistence fails.
pub async fn mark_execution_intent_recoverable(&self, intent_id: i64) -> anyhow::Result<()> {
let mut transaction = self.pool.begin().await.map_err(|e| {
anyhow::anyhow!("Failed to start recoverable execution transition: {e}")
})?;
let current_status = sqlx::query_scalar::<_, String>(
"SELECT status FROM execution_intent WHERE id = $1 FOR UPDATE",View on GitHub (pinned to 2114cf6f76)
Solutions
- Retry the assignment with the same nonce - it is idempotent while status is 'prepared' and nonce is NULL or equal
- Assign the nonce before enabling watchers on the intent to avoid row-lock races
- Raise statement_timeout if lock waits dominate the wrapped {e}
Defensive patterns
Strategy: retry
Try / catch
for attempt in 0..3 {
match db.assign_execution_intent_nonce(intent_id, nonce).await {
Ok(()) => break,
Err(e) if e.downcast_ref::<sqlx::Error>().is_some_and(|se| matches!(se, sqlx::Error::Io(_) | sqlx::Error::ConnectionClosed(_) | sqlx::Error::Database(_))) => {
tokio::time::sleep(Duration::from_millis(50u64 << attempt)).await;
}
Err(e) => return Err(e),
}
} Prevention
- Assign the nonce before starting watchers on the intent to avoid row-lock races
- Retry with the identical nonce - the assignment is idempotent while status is 'prepared'
- Keep nonce assignment on the same task that owns the intent lifecycle
When it happens
Trigger: A concurrent status transition holding the intent row lock while the nonce is assigned; connection drop mid-UPDATE; aggressive statement_timeout.
Common situations: The signer racing the receipt watcher on the same intent; busy nodes with tight timeouts.
Related errors
- Execution intent {intent_id} is not prepared for nonce {nonc
- Failed to lock recoverable execution intent: {e}
- Failed to lock execution intent {intent_id}: {e}
- Failed to start execution schema migration: {e}
- Failed to lock legacy execution transactions: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21).
Data as JSON: /api/errors/b5ba6cdf092f0ef8.
Report an issue: GitHub.