nautechsystems/nautilus_trader · error · anyhow::Error

Stale quote: pool state at block {profiler_block}, latest bl

Error message

Stale quote: pool state at block {profiler_block}, latest block {latest_block}, exceeds `max_quote_age_blocks` {max_age_blocks}

What it means

validate_quote_age also enforces that the quote's age, computed as latest_block - profiler_block, does not exceed max_age_blocks. This error means the pool state backing the quote is older than the configured staleness budget, so the quote is considered too stale to execute against and is rejected.

Source

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

        pool.dex.flash_created_event.as_deref(),
        pool.dex.fee_protocol_update_event.as_deref(),
        pool.dex.fee_protocol_collect_event.as_deref(),
    ]
    .into_iter()
    .flatten()
}

fn validate_quote_age(
    profiler_block: u64,
    latest_block: u64,
    max_age_blocks: u64,
) -> anyhow::Result<()> {
    anyhow::ensure!(
        profiler_block <= latest_block,
        "Pool state at block {profiler_block} is ahead of the latest block {latest_block}; the execution RPC endpoint lags the data feed"
    );
    let quote_age = latest_block - profiler_block;
    anyhow::ensure!(
        quote_age <= max_age_blocks,
        "Stale quote: pool state at block {profiler_block}, latest block {latest_block}, exceeds `max_quote_age_blocks` {max_age_blocks}"
    );
    Ok(())
}

fn validate_rpc_transaction_matches_payload(
    transaction: &RpcTransaction,
    raw_transaction: &[u8],
) -> anyhow::Result<()> {
    let signed = decode_signed_transaction(raw_transaction)?;
    anyhow::ensure!(
        transaction.hash == signed.hash
            && transaction.from == signed.signer
            && transaction.nonce == signed.nonce
            && transaction.chain_id == Some(signed.chain_id)
            && transaction.transaction_type == Some(2)
            && transaction.to == Some(signed.to)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Re-fetch pool state at the latest block and rebuild the quote before executing.
  2. Increase max_quote_age_blocks to a realistic bound for your feed latency, if the current value is stricter than your SLA requires.
  3. Reduce pool-state refresh interval or add a feed-health alert so stale snapshots are not reused.
  4. Check the data feed for outages/gaps causing stale cached pool state.

Example fix

// before: stale cached quote reused
let quote = cached_pool_quote(pool);
validate_quote_age(quote.block, latest_block, cfg.max_quote_age_blocks)?;

// after: refresh when stale
if latest_block - cached_pool_quote(pool).block > cfg.max_quote_age_blocks {
    refresh_pool_quote(&mut cache, pool, latest_block).await?;
}
let quote = cached_pool_quote(pool);
validate_quote_age(quote.block, latest_block, cfg.max_quote_age_blocks)?;
Defensive patterns

Strategy: validation

Validate before calling

let latest_block = exec_rpc.get_block_number().await?;
if latest_block.saturating_sub(quote_block) > cfg.max_quote_age_blocks {
    refresh_pool_quote(pool, latest_block).await?;
}

Try / catch

match client.get_pool_quote(pool).await {
    Ok(q) => q,
    Err(e) if e.to_string().contains("Stale quote") => {
        refresh_pool_quote(pool, latest_block).await?;
        client.get_pool_quote(pool).await
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling validate_quote_age where (latest_block - profiler_block) > max_age_blocks — i.e., the pool state snapshot used for the quote is many blocks behind the execution endpoint's latest block.

Common situations: max_quote_age_blocks configured too tightly for feed latency; slow pool-state refresh loop; data feed outage/backfill leaving stale cache; bursty block times making a normally-acceptable age exceed the limit.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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