nautechsystems/nautilus_trader · error · anyhow::Error

Active execution intent {intent_id} was not found

Error message

Active execution intent {intent_id} was not found

What it means

Raised in add_execution_replacement_hash (database.rs:3800) when the locking SELECT ... WHERE id = $1 AND active returns no row: the intent either does not exist or is no longer active. The active flag is flipped to false by terminal processing (record_execution_status sets active=false once finalized/reverted and both event markers are emitted, and mark_execution_event_emitted deactivates on terminal markers), so 'not found' here usually means the intent already completed.

Source

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

    pub async fn add_execution_replacement_hash(
        &self,
        intent_id: i64,
        chain_id: u32,
        transaction_hash: &str,
    ) -> anyhow::Result<ExecutionTransactionHashRow> {
        let chain_id_db = i32::try_from(chain_id)
            .with_context(|| format!("Chain ID {chain_id} exceeds PostgreSQL INTEGER"))?;
        let mut transaction = self.pool.begin().await.map_err(|e| {
            anyhow::anyhow!("Failed to start replacement transaction persistence: {e}")
        })?;
        let current_status = sqlx::query_scalar::<_, String>(
            "SELECT status FROM execution_intent WHERE id = $1 AND active FOR UPDATE",
        )
        .bind(intent_id)
        .fetch_optional(&mut *transaction)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to lock active execution intent {intent_id}: {e}"))?
        .ok_or_else(|| anyhow::anyhow!("Active execution intent {intent_id} was not found"))?;
        anyhow::ensure!(
            execution_transition_allowed(&current_status, TransactionStatus::Replaced),
            "Invalid execution transition for intent {intent_id}: {current_status} -> replaced"
        );

        sqlx::query(
            "
            UPDATE execution_transaction_hash
            SET current = FALSE, status = 'replaced', updated_at = NOW()
            WHERE intent_id = $1 AND current
            ",
        )
        .bind(intent_id)
        .execute(&mut *transaction)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to retire replaced execution hash: {e}"))?;

        let row = sqlx::query_as::<_, ExecutionTransactionHashRow>(

View on GitHub (pinned to 2114cf6f76)

Solutions

  1. Check the intent's actual state: SELECT status, active, fill_emitted, terminal_emitted FROM execution_intent WHERE id = $1
  2. If the intent is finalized/reverted, treat the replacement observation as stale and skip it (the transaction never started mutating anything)
  3. If the id simply does not exist, fix the caller that produced it (re-resolve via get_active_execution_intent instead of caching ids)
  4. If active=false but status is non-terminal, investigate what deactivated the row before overriding anything

Example fix

// before: a late replacement observation crashes the watcher
let row = db.add_execution_replacement_hash(intent_id, chain_id, &hash).await?;

// after: treat 'no active intent' as a benign stale event
match db.add_execution_replacement_hash(intent_id, chain_id, &hash).await {
    Ok(row) => Ok(Some(row)),
    Err(e) if e.to_string().contains("was not found") => Ok(None), // intent already completed
    Err(e) => Err(e),
}
Defensive patterns

Strategy: validation

Validate before calling

// pre-check: is the intent still active before recording a late replacement?
let row = sqlx::query_as::<_, (bool,)>("SELECT active FROM execution_intent WHERE id = $1")
    .bind(intent_id)
    .fetch_optional(&pool)
    .await?;
if !row.is_some_and(|(active,)| active) {
    // intent absent or completed: skip the stale replacement event
    return Ok(None);
}

Try / catch

match db.add_execution_replacement_hash(intent_id, chain_id, &hash).await {
    Ok(row) => Ok(Some(row)),
    Err(e) if e.to_string().contains("was not found") => Ok(None), // stale: already terminal/inactive
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Recording a replacement hash after the intent was finalized and its fill/terminal event was marked emitted (active=false); passing an intent_id that was never created or belongs to another database; the intent deactivated by a concurrent transaction that committed first.

Common situations: A reorg/replacement race where the finality watcher wins and the replacement watcher arrives late; replaying queued replacement events after a restart against an intent that finished meanwhile; mixed-up intent ids when several wallets trade the same pool.

Related errors


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