nautechsystems/nautilus_trader · error · anyhow::Error

Failed to record execution transition

Error message

Failed to record execution transition: {e}

What it means

Raised when the INSERT into the execution transition audit/history table (recording from_status -> to_status with block number/hash) fails inside the transaction. All state updates succeeded, but persisting the audit trail errored, so the whole transition rolls back to keep state and history consistent.

Solutions

  1. Read the inner `{e}`; if it names a missing relation, apply pending migrations for the transition-history table.
  2. Check NULL handling: ensure block_number_db/block_hash are nullable or defaulted in the schema for pre-finality transitions.
  3. Retry the full transition with backoff on transient connection errors.
  4. Check database disk space and write capacity during receipt-processing bursts.

Example fix

// before
.bind(block_number_db)
.bind(block_hash)
.execute(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to record execution transition: {e}"))?;
// after
.bind(block_number_db) // ensure column allows NULL for pending transitions
.bind(block_hash)
.execute(&mut *transaction)
.await
.with_context(|| format!("Failed to record execution transition {current_status} -> {} for intent", status.as_str()))?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the history table exists and allows NULL block numbers
sqlx::query("SELECT 1 FROM execution_transition_history LIMIT 1").execute(&pool).await?;
// and check column nullability in information_schema for block_number

Try / catch

if let Err(e) = transition.await {
    let msg = e.to_string();
    if msg.contains("Failed to record execution transition") {
        if msg.contains("does not exist") { run_migrations()?; }
        else if msg.contains("null value") { fix_null_block_number_path()?; }
        else { retry_with_backoff(); }
    }
}

Prevention

When it happens

Trigger: Calling the transition when the history INSERT errors: missing transition-history table (migration not run), NOT NULL violation (e.g. block_number_db bound as NULL into a non-null column), connection failure, or sequence/serial exhaustion.

Common situations: Deploying a version that writes history before applying its migration; NULL block_number for mempool/pending transitions hitting a NOT NULL column; database disk full or connection drop during heavy event bursts.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/50d3c5a7aaac92d0. Report an issue: GitHub.

Appendix: source

Thrown at crates/adapters/blockchain/src/cache/database.rs:6876

                intent_id, transaction_hash_id, transition_key, from_status, to_status,
                block_number, block_hash
            )
            SELECT $1, id, $3, $4, $5, $6, $7
            FROM execution_transaction_hash
            WHERE intent_id = $1 AND transaction_hash = $2
            ON CONFLICT (intent_id, transition_key) DO NOTHING
            ",
        )
        .bind(intent_id)
        .bind(transaction_hash)
        .bind(transition_key)
        .bind(current_status)
        .bind(status.as_str())
        .bind(block_number_db)
        .bind(block_hash)
        .execute(&mut *transaction)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to record execution transition: {e}"))?;

        transaction
            .commit()
            .await
            .map_err(|e| anyhow::anyhow!("Failed to commit execution transition: {e}"))?;
        Ok(())
    }

    /// Records verified final consumption and advances the canonical nonce in one transaction.
    ///
    /// # Errors
    ///
    /// Returns an error if the receipt transition, nonce ledger, manifest identity, or evidence
    /// is inconsistent, or if persistence fails.
    pub(crate) async fn record_execution_finality_verified(
        &self,
        finality: &ExecutionFinalityTransition<'_>,
    ) -> anyhow::Result<()> {

View on GitHub (pinned to 18893faf8b)