nautechsystems/nautilus_trader · error · anyhow::Error

No valid legs loaded for BAG contract

Error message

No valid legs loaded for BAG contract

What it means

After iterating the BAG contract's combo legs, fetch_bag_contract requires at least one leg to have resolved successfully (each leg's own contract details must be fetched and parsed into an instrument ID). If every leg failed to load, leg_tuples is empty and there is no basis to construct the spread, so the call fails.

Source

Thrown at crates/adapters/interactive_brokers/src/providers/instruments.rs:2450

            // Get the contract details for this leg (should be cached now)
            let leg_details_clone = self
                .contract_details
                .get(&leg_instrument_id)
                .map(|entry| entry.value().clone())
                .ok_or_else(|| {
                    anyhow::anyhow!(
                        "Contract details not found for leg {} after loading",
                        leg_instrument_id
                    )
                })?;

            leg_contract_details.push((leg_details_clone, ratio));
            leg_tuples.push((leg_instrument_id, ratio));
        }

        if leg_tuples.is_empty() {
            anyhow::bail!("No valid legs loaded for BAG contract");
        }

        // Create spread instrument ID from leg tuples
        let spread_instrument_id = create_spread_instrument_id(&leg_tuples)
            .context("Failed to create spread instrument ID from leg tuples")?;

        // Fetch BAG contract details (for storing the mapping)
        let bag_details_vec = client
            .contract_details(bag_contract)
            .await
            .context("Failed to fetch BAG contract details from IB")?;

        if bag_details_vec.is_empty() {
            tracing::warn!("No contract details returned for BAG contract");

            if bag_contract.contract_id != 0 && self.instruments.contains_key(&spread_instrument_id)
            {
                self.contract_id_to_instrument_id

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect debug logs for per-leg failures; fix the underlying leg resolution errors (bad con_ids, missing permissions).
  2. Validate each leg's con_id is live on IB (check in TWS).
  3. Re-run after confirming IB Gateway connectivity and market-data permissions for each leg's exchange.
  4. If the spread is stale (legs expired), redefine the spread with current leg contracts.

Example fix

// before: spread with expired leg con_ids
let legs = vec![ComboLeg { con_id: 1, ratio: 1, action: "BUY".into(), ..Default::default() }];
// after: valid, current leg con_ids
let legs = vec![
    ComboLeg { con_id: 552824014, ratio: 1, action: "BUY".into(), ..Default::default() },
    ComboLeg { con_id: 552824046, ratio: 1, action: "SELL".into(), ..Default::default() },
];
Defensive patterns

Strategy: validation

Validate before calling

fn legs_look_valid(contract: &Contract) -> bool {
    contract.combo_legs.iter().all(|l| l.con_id != 0 && l.ratio > 0)
}

Prevention

When it happens

Trigger: All individual legs of a BAG contract failed during leg loading — e.g., each leg's con_id lookup returned nothing or leg instrument IDs could not be resolved — leaving leg_tuples empty.

Common situations: Expired or invalid leg con_ids, network/permissions failures silently skipping legs, or legs whose symbols/exchanges don't resolve on IB.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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