nautechsystems/nautilus_trader · error

Finalized Swap input {quote_amount} does not match the persi

Error message

Finalized Swap input {quote_amount} does not match the persisted amount {}

What it means

For BUY orders, the finalized Swap event's quote-side amount must be positive and equal in absolute value to the persisted plan.amount_in, since for a BUY the quote token is the input. A mismatch means the on-chain quote input differs from the recorded plan amount.

Source

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

        })?;
    let event = dex.parse_swap_event_rpc(log)?;
    let (base_amount, quote_amount) =
        if plan.pool.get_base_token().address == plan.pool.token0.address {
            (event.amount0, event.amount1)
        } else {
            (event.amount1, event.amount0)
        };
    let last_qty = match plan.order.order_side() {
        OrderSide::Sell => {
            anyhow::ensure!(
                base_amount.is_positive() && base_amount.unsigned_abs() == plan.amount_in,
                "Finalized Swap input {base_amount} does not match the persisted amount {}",
                plan.amount_in
            );
            plan.order.quantity()
        }
        OrderSide::Buy => {
            anyhow::ensure!(
                quote_amount.is_positive() && quote_amount.unsigned_abs() == plan.amount_in,
                "Finalized Swap input {quote_amount} does not match the persisted amount {}",
                plan.amount_in
            );
            anyhow::ensure!(
                base_amount.is_negative(),
                "Finalized Swap base amount {base_amount} is not a BUY output"
            );
            raw_amount_to_quantity(
                base_amount.unsigned_abs(),
                plan.pool.get_base_token().decimals,
            )?
        }
    };

    let block = &included.finality.inclusion_header;
    anyhow::ensure!(
        block.number == included.block_number

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check that the base/quote selection branch matches the pool's token ordering
  2. Persist amount_in with the same decimals/units the RPC decoder emits
  3. If the route splits input across multiple pools, sum per-pool inputs rather than comparing plan.amount_in to one leg
  4. Verify the log corresponds to this plan's swap (tx hash, log index) before comparing amounts
Defensive patterns

Strategy: validation

Validate before calling

if order_side == OrderSide::Buy && !(quote_amount.is_positive() && quote_amount.unsigned_abs() == plan.amount_in) {
    return Err(anyhow!("BUY input mismatch: event={} plan={}", quote_amount, plan.amount_in));
}

Try / catch

if let Err(e) = verify_finalized_swap(...).await {
    if e.to_string().contains("does not match the persisted amount") {
        reconcile_with_route_splits(tx_hash).await?;
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: Verifying a BUY swap where the selected quote amount is <= 0 or unsigned_abs() != plan.amount_in — caused by wrong base/quote amount mapping (amount0 vs amount1), a plan whose recorded input was changed, or the log belonging to a different swap in the same transaction.

Common situations: Token0/token1 ordering confusion between pool metadata and decoder output; amount_in persisted in different units (wei vs token decimals); executing via an aggregator that split the input across pools so this pool's input is smaller than the plan total.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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