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_inView on GitHub (pinned to 18893faf8b)
Solutions
- Register the DEX in the exchanges registry so get_dex_extended(chain, dex) returns a decoder implementing parse_swap_event_rpc
- Fix the pool's chain.name/dex.name to exactly match the registered registry keys (case-sensitive)
- Implement the RPC Swap decoder for the missing DEX if it does not exist yet
- 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
- Validate at startup that every tradable pool's chain/DEX has a registered decoder
- Keep pool metadata DEX names in sync with registry keys (exact case)
- Implement parse_swap_event_rpc when onboarding a new DEX
- Gate pool selection on decoder availability
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
- Failed to load active execution intent: {e}
- Failed to start replacement transaction persistence: {e}
- Failed to lock active execution intent {intent_id}: {e}
- Active execution intent {intent_id} was not found
- Invalid execution transition for intent {intent_id}: {curren
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/80f6e80137c81480.
Report an issue: GitHub.