nautechsystems/nautilus_trader · error

Blockchain execution transaction limits are required…

Error message

Blockchain execution transaction limits are required: allowed_token_pairs, slippage_bps, max_slippage_bps, max_order_amount, deadline_seconds, max_quote_age_blocks, receipt_timeout_secs

What it means

transaction_limits() requires a fully populated set of execution risk limits. If any of allowed_token_pairs, slippage_bps, max_slippage_bps, max_order_amount, deadline_seconds, max_quote_age_blocks, or receipt_timeout_secs is absent from the config, the else-branch of the destructuring let bails with this message enumerating all required fields.

Solutions

  1. Populate all seven listed fields in the config: allowed_token_pairs, slippage_bps, max_slippage_bps, max_order_amount, deadline_seconds, max_quote_age_blocks, receipt_timeout_secs
  2. Diff the config against the current ExecutionClientConfig struct to find newly required fields
  3. Check release notes for config schema changes after an upgrade

Example fix

// before
[execution]
slippage_bps = 50
// after
[execution]
allowed_token_pairs = [["0x...IN", "0x...OUT"]]
slippage_bps = 50
max_slippage_bps = 200
max_order_amount = "1000000000000000000"
deadline_seconds = 120
max_quote_age_blocks = 10
receipt_timeout_secs = 60
Defensive patterns

Strategy: validation

Validate before calling

let required = [
    ("allowed_token_pairs", cfg.allowed_token_pairs.is_none()),
    ("slippage_bps", cfg.slippage_bps.is_none()),
    ("max_slippage_bps", cfg.max_slippage_bps.is_none()),
    ("max_order_amount", cfg.max_order_amount.is_none()),
    ("deadline_seconds", cfg.deadline_seconds.is_none()),
    ("max_quote_age_blocks", cfg.max_quote_age_blocks.is_none()),
    ("receipt_timeout_secs", cfg.receipt_timeout_secs.is_none()),
];
let missing: Vec<_> = required.iter().filter(|(_, m)| *m).map(|(n, _)| *n).collect();
if !missing.is_empty() { panic!("missing execution limit fields: {missing:?}"); }

Try / catch

match BlockchainExecutionClient::new(config, provider).await {
    Err(e) if e.to_string().contains("transaction limits are required") => {
        // surface missing-field list to the operator, abort startup
    }
    Err(e) => return Err(e),
    Ok(client) => client,
}

Prevention

When it happens

Trigger: Calling BlockchainExecutionClient::new (which invokes transaction_limits) with a config where any one of the seven required limit fields is None/missing.

Common situations: Upgrading to a version that added new mandatory fields (e.g. receipt_timeout_secs) while reusing an old config file; hand-written config omitting optional-looking fields; a config parser that maps missing TOML keys to None silently.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

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

        let (
            Some(allowed_token_pairs),
            Some(slippage_bps),
            Some(max_slippage_bps),
            Some(max_order_amount),
            Some(deadline_seconds),
            Some(max_quote_age_blocks),
            Some(receipt_timeout_secs),
        ) = (
            &config.allowed_token_pairs,
            config.slippage_bps,
            config.max_slippage_bps,
            config.max_order_amount,
            config.deadline_seconds,
            config.max_quote_age_blocks,
            config.receipt_timeout_secs,
        )
        else {
            anyhow::bail!(
                "Blockchain execution transaction limits are required: allowed_token_pairs, slippage_bps, max_slippage_bps, max_order_amount, deadline_seconds, max_quote_age_blocks, receipt_timeout_secs"
            );
        };

        let mut parsed_pairs = HashSet::with_capacity(allowed_token_pairs.len());
        for (token_in, token_out) in allowed_token_pairs {
            parsed_pairs.insert((
                validate_address(token_in.as_str())?,
                validate_address(token_out.as_str())?,
            ));
        }

        let quote_spend_limits = config.quote_spend_limits.as_deref().unwrap_or_default();
        let mut parsed_quote_spend_limits = HashMap::with_capacity(quote_spend_limits.len());
        for limit in quote_spend_limits {
            let token_in = validate_address(limit.token_in.as_str())?;
            let token_out = validate_address(limit.token_out.as_str())?;
            let spend_token = validate_address(limit.spend_token.as_str())?;

View on GitHub (pinned to 18893faf8b)