nautechsystems/nautilus_trader · error
Failed to assign verified execution nonce: {e}
Error message
Failed to assign verified execution nonce: {e} What it means
The final `UPDATE execution_intent SET nonce = $2 ... WHERE id = $1 AND status = 'prepared' AND active AND (nonce IS NULL OR nonce = $2)` matched zero rows (or the execute itself failed). If `.execute` errors, this message wraps the SQLx error; the code also throws it via the following `rows_affected() == 1` ensure when the WHERE predicate filtered the row out. The library throws it because the nonce write must land on a prepared, active intent that owns no conflicting nonce.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:6029
.await
.map_err(|e| anyhow::anyhow!("Failed to persist pre-sign verification: {e}"))?;
}
let result = sqlx::query(
"
UPDATE execution_intent
SET nonce = $2, updated_at = NOW()
WHERE id = $1
AND status = 'prepared'
AND active
AND (nonce IS NULL OR nonce = $2)
",
)
.bind(assignment.intent_id)
.bind(nonce)
.execute(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to assign verified execution nonce: {e}"))?;
anyhow::ensure!(
result.rows_affected() == 1,
"Execution intent {} is not prepared for canonical nonce {}",
assignment.intent_id,
assignment.nonce
);
transaction
.commit()
.await
.map_err(|e| anyhow::anyhow!("Failed to commit verified nonce assignment: {e}"))?;
Ok(())
}
/// Appends one verified decision batch before an action on an existing active intent.
pub(crate) async fn record_execution_verification_batch(
&self,
batch: &ExecutionVerificationBatch<'_>,
) -> anyhow::Result<()> {View on GitHub (pinned to 18893faf8b)
Solutions
- Treat it as a lost race: re-read the intent's current status/nonce, and if the nonce was already assigned to the same value, treat the operation as idempotent success; otherwise restart the assignment flow.
- If the SQLx error is a permissions error (42501), grant UPDATE on execution_intent to the application role.
- Keep all mutation on the intent inside this transaction (hold the FOR UPDATE lock) so status cannot change mid-assignment; avoid external writers bypassing the API.
- Inspect the inner `{e}` when present for the concrete driver-level cause (connection, permission, trigger).
Example fix
// before: treating every failure as fatal
match db.assign_execution_intent_nonce_verified(&assignment).await {
Err(e) => return Err(e),
Ok(()) => {}
}
// after: idempotent handling of concurrent assignment
match db.assign_execution_intent_nonce_verified(&assignment).await {
Ok(()) => {}
Err(e) if intent_already_has_nonce(&db, assignment.intent_id, nonce).await => { /* already assigned */ }
Err(e) => return Err(e),
} Defensive patterns
Strategy: try-catch
Validate before calling
let (status, active, nonce): (String, bool, Option<i64>) = sqlx::query_as(
"SELECT status, active, nonce FROM execution_intent WHERE id = $1")
.bind(&assignment.intent_id).fetch_optional(pool).await?
.ok_or_else(|| anyhow!("intent missing"))?;
anyhow::ensure!(status == "prepared" && active && (nonce.is_none() || nonce == Some(assignment.nonce)), "intent not assignable"); Try / catch
match db.assign_execution_intent_nonce_verified(&assignment).await {
Err(e) if e.to_string().contains("Failed to assign verified execution nonce")
|| e.to_string().contains("is not prepared for canonical nonce") => {
// lost race or lost update: re-read intent and either treat as idempotent success or restart the flow
}
other => other?,
} Prevention
- Never mutate execution_intent status/nonce outside the provided database API.
- Do not manually flip status/active in the database while assignment flows are running.
- Grant the application role UPDATE privilege on execution_intent.
- Design callers to be idempotent: a re-assignment with the same nonce should be a no-op, not a retry storm.
When it happens
Trigger: The intent's status changed from "prepared" between the earlier SELECT and this UPDATE (another transaction assigned/executed/cancelled it); the intent was deactivated; the intent already holds a different nonce; or the UPDATE fails outright (connection loss, permission denied on UPDATE, trigger abort).
Common situations: A concurrent nonce-assignment or cancellation racing the FOR UPDATE window (e.g. from a different connection outside the lock, or trigger-side effects); admin manually flipping status/active in the DB; running with a role lacking UPDATE privilege on execution_intent; long transactions causing the DBA to kill the session.
Related errors
- Failed to reconstruct migrated hash: {e}
- Failed to reconstruct migrated intent: {e}
- Execution intent {} was not found
- Could not calculate schema dir from current directory path o
- Error executing statement {sql_statement} with error: {e:?}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/76a3746039556fd1.
Report an issue: GitHub.