nautechsystems/nautilus_trader · error · anyhow::Error
Finalized Swap input {base_amount} does not match the persis
Error message
Finalized Swap input {base_amount} does not match the persisted amount {} What it means
Reconciliation guard in emit_finalized_swap_fill: it decodes the finalized Swap event, picks amount0 or amount1 depending on which pool token is the base token, and requires that value to be positive and equal to plan.amount_in. A mismatch means the on-chain swap input differs from the amount persisted with the execution intent, so the fill cannot be attributed to this order.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:2249
"Finalized Swap log came from pool {address}, expected {}",
plan.pool_address
);
let dex = crate::exchanges::get_dex_extended(plan.pool.chain.name, &plan.pool.dex.name)
.ok_or_else(|| {
anyhow::anyhow!(
"No RPC Swap decoder for {}:{}",
plan.pool.chain.name,
plan.pool.dex.name
)
})?;
let event = dex.parse_swap_event_rpc(log)?;
let base_amount = if plan.pool.get_base_token().address == plan.pool.token0.address {
event.amount0
} else {
event.amount1
};
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
);
let block = executor
.http_rpc_client
.block_by_number(included.block_number, false)
.await?;
anyhow::ensure!(
block.hash == included.receipt.block_hash,
"Finalized block {} changed from {} to {} before fill emission",
included.block_number,
included.receipt.block_hash,
block.hash
);
let timestamp_ns = block
.timestampView on GitHub (pinned to 2114cf6f76)
Solutions
- Compare the Swap event input on the block explorer with the calldata of the transaction hash: decide whether the chain or the persisted plan is wrong.
- If the plan is stale (replacement/resubmission), reconcile the intent record so amount_in matches the landed transaction, then let the fill retry.
- Verify the pool's token0/token1 order and get_base_token() against the on-chain pool contract.
- Check that the decoder applies the correct decimals for the base token.
Defensive patterns
Strategy: try-catch
Validate before calling
let event = dex.parse_swap_event_rpc(&swap_log)?;
let base_in = if pool.get_base_token().address == pool.token0.address { event.amount0 } else { event.amount1 };
if !(base_in.is_positive() && base_in.unsigned_abs() == plan.amount_in) {
log::error!("intent {} amount {} != on-chain input {base_in}; investigate before retry", intent_id, plan.amount_in);
} Try / catch
Catch the mismatch, freeze the intent (do not auto-retry with a 'corrected' amount), alert an operator, and reconcile the persisted plan against the landed transaction before reprocessing.
Prevention
- Persist the exact submitted raw amount at signing time so plan.amount_in can never drift from the broadcast transaction.
- Block replacement/resubmission of an intent with a different amount_in; force a new intent instead.
- Verify pool token0/token1 and base-token mapping at startup against the on-chain pool.
When it happens
Trigger: The intent was replaced/resubmitted with a different amount_in than the transaction that finally landed; the pool's token0/token1 or base-token mapping is registered backwards so the wrong leg is compared; decimals differences between the decoded event and the persisted raw amount; a partial or modified swap produced by an external actor (e.g., MEV re-signing).
Common situations: Replacement transactions (bumped fee) landing after the plan was updated; pool metadata imported with token0/token1 swapped; test fixtures quoting one amount but signing a transaction with another (swap_rpc_state_for_mismatch).
Related errors
- Finalized transaction {} emitted {} Swap logs; expected exac
- Execution intent {} uses unsupported schema version {}
- Execution intent {} has unknown purpose {}
- Active execution intent {} has no nonce
- Finalized execution transaction {tx_hash} no longer has a re
AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21).
Data as JSON: /api/errors/1bdfd3f1412ec4c4.
Report an issue: GitHub.