nautechsystems/nautilus_trader · error
Verified action nonce does not match the active intent
Error message
Verified action nonce does not match the active intent
What it means
record_execution_verification_batch appends verified-action evidence for an active execution intent. It locks the intent row and compares the batch nonce with the nonce stored on the intent; if they differ, the evidence batch is not authorized for that intent and this ensure! aborts the transaction.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:6101
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}"))?;
for (index, decision) in batch.decisions.iter().enumerate() {
let height_start = decisionView on GitHub (pinned to 18893faf8b)
Solutions
- Reload the active intent for intent_id and use its nonce when constructing the ExecutionVerificationBatch
- Verify the batch is being applied to the intent created for this nonce; if the intent was superseded, address the replacement scan flow instead
- Check that no concurrent writer advanced or reset the intent nonce between batch creation and submission
- Inspect execution_intent.nonce in the database and reconcile the caller's nonce ledger
Example fix
// before
let batch = ExecutionVerificationBatch { intent_id, nonce: last_used_nonce, .. };
db.record_execution_verification_batch(&batch).await?;
// after
let intent_nonce: Option<i64> = sqlx::query_scalar(
"SELECT nonce FROM execution_intent WHERE id = $1 AND active",
).bind(intent_id).fetch_one(&db.pool).await?;
let batch = ExecutionVerificationBatch { intent_id, nonce: intent_nonce.expect("active intent nonce") as u64, .. };
db.record_execution_verification_batch(&batch).await?; Defensive patterns
Strategy: validation
Validate before calling
let intent_nonce: Option<i64> = sqlx::query_scalar(
"SELECT nonce FROM execution_intent WHERE id = $1 AND chain_id = $2 AND wallet_address = $3 AND active",
).bind(batch.intent_id).bind(chain_id).bind(batch.wallet_address)
.fetch_one(&pool).await?;
if intent_nonce != Some(batch.nonce as i64) {
return Err(anyhow::anyhow!("skip batch: nonce {batch:?} does not match intent {intent_nonce:?}"));
} Type guard
fn nonce_matches(intent_nonce: Option<i64>, batch_nonce: u64) -> bool {
i64::try_from(batch_nonce).ok() == intent_nonce
} Try / catch
match db.record_execution_verification_batch(&batch).await {
Err(e) if e.to_string().contains("nonce does not match") => {
// reload intent, refresh nonce, rebuild batch
}
Err(e) => return Err(e),
Ok(()) => {}
} Prevention
- Always construct batches from a freshly loaded intent row, not cached state
- Track nonce progression in one place in the caller
- Re-check intent state after process restarts or replays
- Never reuse a batch across intent generations
When it happens
Trigger: Calling record_execution_verification_batch with an ExecutionVerificationBatch whose `nonce` does not equal the nonce stored in execution_intent for `batch.intent_id` (including when the intent row has a NULL nonce).
Common situations: Replaying or reordering batches after a process restart; building the batch from a stale intent record; the intent was replaced/cancelled and re-created with a new nonce; off-by-one or u64/i64 nonce conversion mistakes in the caller.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Verified finalized transaction count advanced without an act
- Account event does not exist for account: {}
- Execution intent {} is not prepared for canonical nonce {}
- Implement FromRow for FuturesSpread
- Implement FromRow for OptionSpread
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ae217240d817b5dd.
Report an issue: GitHub.