nautechsystems/nautilus_trader · critical

Router {} reports an unexpected factory

Error message

Router {} reports an unexpected factory

What it means

Raised during pre-trade router verification when the swap router's factory() call returns an address different from the plan.factory recorded in the execution plan. This guards against the router not being the expected Uniswap V3 router or pointing to a cloned/malicious factory. Verification fails before any swap is submitted.

Source

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

    let factory_call = UniswapV3RouterState::factoryCall.abi_encode();
    let router_factory = required_verification(
        executor
            .verification
            .verify_decoded_call(
                None,
                &plan.router,
                U256::ZERO,
                &factory_call,
                block,
                |result| {
                    UniswapV3RouterState::factoryCall::abi_decode_returns(result)
                        .map_err(Into::into)
                },
            )
            .await,
        "swap router factory",
    )?;
    anyhow::ensure!(
        router_factory.value == plan.factory,
        "Router {} reports an unexpected factory",
        plan.router,
    );
    decisions.push(verification_decision(
        &router_factory,
        Some(block),
        Some(block),
    ));

    let weth_call = UniswapV3RouterState::WETH9Call.abi_encode();
    let router_weth = required_verification(
        executor
            .verification
            .verify_decoded_call(
                None,
                &plan.router,
                U256::ZERO,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Rebuild the execution plan so plan.factory matches the factory reported by the router currently deployed at plan.router
  2. Confirm plan.router and plan.factory are from the same network/deployment (chain ID check)
  3. Re-fetch canonical Uniswap V3 router and factory addresses from official deployments rather than manual entry
  4. If the router is behind a proxy, verify the proxy admin/upgrades did not change factory()

Example fix

// before
plan.router = "0xE592427A0AEce92De3Edee1F18E0157C05861564"; // plan stale after redeploy
// after
let plan = build_execution_plan(chain_id).await?; // re-derive router/factory/weth for current deployment
Defensive patterns

Strategy: validation

Validate before calling

async fn check_router_factory(router: Address, plan_factory: Address) -> Result<(), String> {
    let actual: Address = call_router_factory(router).await?;
    if actual != plan_factory { return Err(format!("router {router} factory mismatch: {actual} != {plan_factory}")); }
    Ok(())
}

Type guard

fn factory_matches(plan_factory: Address, actual: Address) -> bool { plan_factory == actual }

Prevention

When it happens

Trigger: Executing a swap plan where on-chain router.factory() != plan.factory, e.g. the plan was built against a different router deployment or the router contract was upgraded/replaced at plan.router.

Common situations: Stale execution plan reused after a router redeploy, plan built on one network and replayed on another (different contract addresses), router address typo'd in the plan, or a malicious/proxy router.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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