nautechsystems/nautilus_trader · error · anyhow::Error
Intent cannot make the verified finality transition
Error message
Intent cannot make the verified finality transition
What it means
A state-machine guard: after locking the intent, the code ensure()s that the stored nonce matches the finality nonce and that `execution_transition_allowed(¤t_status, finality.status)` permits the requested transition. Either the nonce is stale/wrong or the intent is in a status from which the target verified-finality status is not reachable. This prevents duplicate, out-of-order, or illegal finality transitions.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:7058
}
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")?;
let transition_key = format!(
"finality:{}:{}:{index}",View on GitHub (pinned to 18893faf8b)
Solutions
- Log current_status, finality.status, intent_nonce and expected nonce to see which condition failed
- If the event was already applied, treat it as a duplicate and skip (the transition already happened)
- Re-derive the nonce on the finality message and confirm it matches the intent's stored nonce
- Check execution_transition_allowed's transition table covers the observed current→target status pair
- Ensure single-writer ordering for finality events per intent (or make application idempotent upstream)
Example fix
// before
anyhow::ensure!(
intent_nonce == Some(nonce)
&& execution_transition_allowed(¤t_status, finality.status),
"Intent cannot make the verified finality transition"
);
// after
anyhow::ensure!(
intent_nonce == Some(nonce),
"Intent nonce mismatch: stored={intent_nonce:?} finality={nonce}"
);
anyhow::ensure!(
execution_transition_allowed(¤t_status, finality.status),
"Intent cannot make the verified finality transition: {} -> {}",
current_status, finality.status
); Defensive patterns
Strategy: type-guard
Validate before calling
// Guard the transition before calling the API
fn can_transition(current: &Status, target: &Status) -> bool {
execution_transition_allowed(current, target)
}
if !can_transition(¤t_status, &finality.status) {
// duplicate or out-of-order event: skip instead of failing
} Type guard
fn is_applicable_finality(current_status: &Status, finality: &VerifiedFinality, stored_nonce: Option<i64>, nonce: i64) -> bool {
stored_nonce == Some(nonce)
&& execution_transition_allowed(current_status, &finality.status)
} Try / catch
match apply_verified_finality(...).await {
Err(e) if e.to_string().contains("verified finality transition") => {
// treat as duplicate/out-of-order delivery
info!("finality transition not applicable for intent {}", finality.intent_id);
Ok(())
}
other => other,
} Prevention
- Make finality application idempotent: track applied transition keys and skip repeats
- Deduplicate events at the consumer before invoking the adapter
- Keep a single ordered writer per intent for finality messages
- Update the transition table whenever status lifecycle changes
When it happens
Trigger: Applying the same finality event twice (idempotency guard hit); finality arriving after the intent already moved to a terminal status; a nonce mismatch from replaying an older finality message; a code/config change that reordered statuses so the transition is no longer in the allowed map.
Common situations: At-least-once delivery from the event source re-delivering a processed finality event; two workers processing finality concurrently where the second sees an already-transitioned status; upgrading the adapter while historical events with old nonces are still in flight; mismatched nonce derivation between the intent creator and the finality verifier.
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 finality requires a finalized or reverted status
- Execution intent {intent_id} is not prepared for nonce {nonc
- Invalid execution transition for intent {intent_id}: {curren
- Unsupported `OrderSide` for Binance: {value:?}
- invalid OrderSide: must be Buy or Sell, was {side}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/4839d55b839a63b8.
Report an issue: GitHub.