nautechsystems/nautilus_trader · error · anyhow::Error

Active execution intent {} has no nonce

Error message

Active execution intent {} has no nonce

What it means

An active execution_intent row has nonce = NULL when reconciliation needs it to track the in-flight transaction. The nonce is assigned by assign_execution_intent_nonce() during prepare_and_sign, before signing; an active intent that reached broadcast/pending status must therefore always carry one. NULL means the intent was persisted but the nonce-assignment write never committed, or the row was changed outside the client.

Source

Thrown at crates/adapters/blockchain/src/execution/client.rs:942

        if matches!(intent.status.as_str(), "prepared" | "signed") {
            database
                .mark_execution_intent_recoverable(intent.id)
                .await?;
            release_preparing_slot(&self.in_flight);
            return Ok(());
        }

        let purpose = TransactionPurpose::parse(&intent.purpose).ok_or_else(|| {
            anyhow::anyhow!(
                "Execution intent {} has unknown purpose {}",
                intent.id,
                intent.purpose
            )
        })?;
        let nonce = intent
            .nonce
            .ok_or_else(|| anyhow::anyhow!("Active execution intent {} has no nonce", intent.id))?;
        let hashes = database.get_execution_transaction_hashes(intent.id).await?;
        let current = current_execution_hash(intent.id, &hashes)?;
        let tx_hash = B256::from_str(&current.transaction_hash).with_context(|| {
            format!(
                "Execution intent {} has invalid transaction hash {}",
                intent.id, current.transaction_hash
            )
        })?;
        *self.in_flight.lock().expect("in-flight mutex poisoned") =
            Some(InFlightSlot::AwaitingFinality(InFlightTransaction {
                intent_id: intent.id,
                nonce,
                tx_hash,
                purpose,
            }));

        let plan = if purpose == TransactionPurpose::Swap {
            Some(self.restore_swap_plan(&intent)?)

View on GitHub (pinned to 2114cf6f76)

Solutions

  1. Inspect the row: SELECT id, status, nonce FROM execution_intent WHERE active AND nonce IS NULL;
  2. If status is 'prepared' or 'signed', the reconciler would have handled it, so for NULL-nonce rows past those statuses the row is invalid: verify on-chain (eth_getTransactionByHash of its current hash) whether anything was broadcast
  3. If nothing was broadcast, mark the intent inactive (active = FALSE) so startup proceeds, and let the slot logic re-claim cleanly
  4. If a transaction did land, repair the nonce from the on-chain transaction and let reconciliation finish

Example fix

-- before: active intent without nonce blocks connect()
SELECT id, status, nonce FROM execution_intent WHERE active AND nonce IS NULL;

-- after: confirm no on-chain tx, then retire the malformed intent
UPDATE execution_intent SET active = FALSE WHERE id = 42 AND nonce IS NULL;
Defensive patterns

Strategy: validation

Validate before calling

-- Active intents past the signed stage must carry a nonce
SELECT id, status, nonce
FROM execution_intent
WHERE active
  AND nonce IS NULL
  AND status NOT IN ('prepared', 'signed');
-- Zero rows expected; otherwise reconcile or retire the row before connect().

Try / catch

try:
    client.connect()
except Exception as e:
    if 'has no nonce' in str(e):
        # check execution_transaction_hash + on-chain state, then repair or deactivate the intent
        ...
    raise

Prevention

When it happens

Trigger: reconcile_unresolved_execution() loads an active intent (active = TRUE) whose status is past prepared/signed (e.g. 'pending' or 'included') and whose nonce column is NULL: a partially written row from a crashed run between INSERT and nonce assignment, manual row edits, or a database restored inconsistently.

Common situations: Process killed or Postgres failover exactly between intent insert and nonce assignment; DBA manually resetting intents; a store shared between two wallet configurations where one tool wrote incomplete rows.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21). Data as JSON: /api/errors/026d690c607c1d94. Report an issue: GitHub.