nautechsystems/nautilus_trader · error

Active replacement scan intent was not found

Error message

Active replacement scan intent was not found

What it means

After validating the nonce ledger, `record_execution_replacement_scan` locks the `execution_intent` row matching (intent_id, chain_id, wallet_address, nonce) that is still `active`, using `FOR UPDATE`. If no such row exists the scan has no live intent to attach evidence to, so the transaction fails with this error.

Source

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

            "Replacement scan conflicts with the canonical nonce or manifest ledger"
        );
        let current_status = sqlx::query_scalar::<_, String>(
            "
            SELECT status
            FROM execution_intent
            WHERE id = $1 AND chain_id = $2 AND wallet_address = $3
              AND nonce = $4 AND active
            FOR UPDATE
            ",
        )
        .bind(scan.intent_id)
        .bind(chain_id)
        .bind(scan.wallet_address)
        .bind(nonce)
        .fetch_optional(&mut *transaction)
        .await
        .context("failed to lock the replacement scan intent")?
        .ok_or_else(|| anyhow::anyhow!("Active replacement scan intent was not found"))?;

        if let Some(cursor) = scan.finalized_cursor {
            let number = i64::try_from(cursor.number)
                .context("Replacement scan cursor exceeds PostgreSQL BIGINT")?;
            let durable_hash = sqlx::query_scalar::<_, String>(
                "
                SELECT hash
                FROM execution_verified_finalized_header
                WHERE chain_id = $1 AND wallet_address = $2 AND number = $3
                ",
            )
            .bind(chain_id)
            .bind(scan.wallet_address)
            .bind(number)
            .fetch_optional(&mut *transaction)
            .await
            .context("failed to validate replacement cursor against finalized headers")?
            .ok_or_else(|| {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Confirm the intent still exists and `active = TRUE` in `execution_intent` for the exact (intent_id, chain_id, wallet_address, nonce) tuple.
  2. Do not resubmit a scan for an intent that has already been finalized or replaced; treat this as a no-op in the caller.
  3. Refresh the intent id from the current submission pipeline instead of reusing a cached one.
  4. Check that the nonce bound to the intent matches the scan nonce.

Example fix

// before: unconditional resubmission of a completed scan
if let Err(e) = db.record_execution_replacement_scan(&scan).await { retry(e); }
// after: check the intent is still active first
if db.is_execution_intent_active(scan.intent_id).await? {
    db.record_execution_replacement_scan(&scan).await?;
}
Defensive patterns

Strategy: validation

Validate before calling

let active = sqlx::query_scalar::<_, bool>("SELECT active FROM execution_intent WHERE id = $1 AND chain_id = $2 AND wallet_address = $3 AND nonce = $4").bind(scan.intent_id).bind(chain_id).bind(scan.wallet_address).bind(scan.nonce as i64).fetch_optional(&pool).await?;
if active != Some(true) { skip_scan(); }

Type guard

async fn intent_is_active(pool: &PgPool, intent_id: i64, chain_id: i32, wallet: &str, nonce: i64) -> anyhow::Result<bool> {
    Ok(sqlx::query_scalar::<_, bool>("SELECT active FROM execution_intent WHERE id=$1 AND chain_id=$2 AND wallet_address=$3 AND nonce=$4")
        .bind(intent_id).bind(chain_id).bind(wallet).bind(nonce).fetch_optional(pool).await?.unwrap_or(false))
}

Try / catch

match db.record_execution_replacement_scan(&scan).await {
    Err(e) if e.to_string().contains("Active replacement scan intent was not found") => info!("intent already finalized; skipping"),
    Err(e) => return Err(e),
    Ok(()) => {},
}

Prevention

When it happens

Trigger: Calling `record_execution_replacement_scan` with an `intent_id` that does not exist, that belongs to a different chain_id/wallet_address/nonce, or whose `active` flag is FALSE (already finalized, replaced, or deactivated).

Common situations: The intent was already marked replaced/recoverable by a prior scan run; a stale intent id was cached by the caller after an earlier scan completed; the scan payload was constructed against a re-keyed wallet or different chain.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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