nautechsystems/nautilus_trader · error

`router_addresses` must contain at least one router address

Error message

`router_addresses` must contain at least one router address

What it means

BlockchainExecutionClient::new validates the router_addresses list from the config before constructing the client. The library throws this when the configured router address collection is empty, because an execution client cannot route swaps without at least one Uniswap-style router contract. Fail-fast happens at construction rather than at first transaction.

Source

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

            http_rpc_client.clone(),
            config.http_rpc_url.expose_secret(),
            verification_config,
            config.rpc_requests_per_second,
        )?;
        let wallet_address = validate_address(config.wallet_address.as_str())?;
        let erc20_contract = Erc20Contract::new_with_timeout(
            http_rpc_client.clone(),
            Some(EXECUTION_RPC_TIMEOUT_SECS),
            true,
        );

        let router_addresses = config
            .router_addresses
            .iter()
            .map(|address| validate_address(address.as_str()))
            .collect::<anyhow::Result<Vec<_>>>()?;
        if router_addresses.is_empty() {
            anyhow::bail!("`router_addresses` must contain at least one router address");
        }
        let weth_address = validate_address(config.weth_address.as_str())?;
        Self::validate_manifest_contracts(&config, &router_addresses, weth_address)?;

        let mut token_universe = HashSet::new();

        if let Some(specified_tokens) = &config.tokens {
            for token in specified_tokens {
                let token_address = validate_address(token.as_str())?;
                token_universe.insert(token_address);
            }
        }
        let wallet_balance = WalletBalance::new(token_universe);
        let emitter = ExecutionEventEmitter::new(
            get_atomic_clock_realtime(),
            core_client.trader_id,
            core_client.account_id,
            core_client.account_type,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Add at least one valid router address to config.router_addresses for the target chain
  2. Verify the config loader is actually populating router_addresses (no silent default to empty vec)
  3. Confirm the addresses belong to the connected network and are checksummed/parseable by validate_address

Example fix

// before
router_addresses = []
// after
router_addresses = ["0xE592427A0AEce92De3Edee1F18E0157C05861564"]
Defensive patterns

Strategy: validation

Validate before calling

if config.router_addresses.is_empty() {
    return Err("router_addresses must contain at least one address before constructing the client".into());
}
for a in &config.router_addresses {
    Address::from_str(a)?; // also fails fast on malformed addresses
}

Type guard

fn has_router_addresses(cfg: &ExecutionClientConfig) -> bool {
    !cfg.router_addresses.is_empty()
}

Prevention

When it happens

Trigger: Calling BlockchainExecutionClient::new (public) with a config whose router_addresses vec/field is empty or omitted, e.g. an unset TOML/env config mapping to Vec::new().

Common situations: Config file missing the router_addresses section; a defaults-loading layer skipping empty lists; operator forgetting to set router addresses per-chain when switching networks.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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