nautechsystems/nautilus_trader · error · anyhow::Error

No RPC Swap decoder for {}:{}

Error message

No RPC Swap decoder for {}:{}

What it means

To decode the raw Swap log into a domain swap event, the library looks up a registered DEX decoder via get_dex_extended(chain_name, dex_name). If no decoder is registered for the plan's chain/DEX pair, the log cannot be parsed and verification aborts.

Source

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

    let log_block_hash = log
        .block_hash
        .as_deref()
        .ok_or_else(|| anyhow::anyhow!("Finalized Swap log has no block hash"))?;
    anyhow::ensure!(
        log_transaction_hash == included.tx_hash
            && rpc_log::extract_block_number(log)? == included.block_number
            && u64::from(rpc_log::extract_transaction_index(log)?)
                == included.receipt.transaction_index
            && B256::from_str(log_block_hash)
                .with_context(|| "Invalid finalized Swap log block hash")?
                == included.receipt.block_hash,
        "Finalized Swap log position does not match transaction {}",
        included.tx_hash
    );

    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, 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

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Register the DEX in the exchanges registry so get_dex_extended(chain, dex) returns a decoder implementing parse_swap_event_rpc
  2. Fix the pool's chain.name/dex.name to exactly match the registered registry keys (case-sensitive)
  3. Implement the RPC Swap decoder for the missing DEX if it does not exist yet
  4. Filter tradable pools at startup to only chain/DEX pairs with registered decoders

Example fix

// before
// dex not registered -> runtime error
// after
exchanges::register_dex_extended("base", "newdex", NewDexDecoder::default());
Defensive patterns

Strategy: fallback

Validate before calling

if crate::exchanges::get_dex_extended(pool.chain.name, pool.dex.name).is_none() {
    return Err(anyhow!("DEX {}:{} not registered", pool.chain.name, pool.dex.name));
}

Try / catch

let dex = get_dex_extended(chain, dex)
    .ok_or_else(|| anyhow!("No RPC Swap decoder for {}: {}", chain, dex))?;

Prevention

When it happens

Trigger: Executing a swap plan on a chain/DEX combination that was never registered in the exchanges registry — e.g. a new DEX added to configuration but not wired into get_dex_extended, or a typo/renamed dex.name in pool metadata.

Common situations: Adding support for a new DEX without implementing parse_swap_event_rpc; pool metadata seeded from an external source with a DEX name that differs in casing/spacing from the registry key; running on a testnet chain that has no registered decoder.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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