nautechsystems/nautilus_trader · error · anyhow::Error

Leg instrument {} not found in contract details after loadin

Error message

Leg instrument {} not found in contract details after loading

What it means

fetch_spread_instrument looks up each combo leg's ContractDetails in the provider's contract_details cache by leg InstrumentId. This error means a required leg was not present in the cache after the provider attempted to load it, so the spread cannot be assembled.

Source

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

            );

            // Load the individual leg instrument
            self.fetch_contract_details(
                client,
                *leg_instrument_id,
                force_instrument_update,
                filters.clone(),
            )
            .await
            .with_context(|| format!("Failed to load leg instrument: {}", leg_instrument_id))?;

            // Get the contract details for this leg
            let leg_details = self
                .contract_details
                .get(leg_instrument_id)
                .map(|entry| entry.value().clone())
                .ok_or_else(|| {
                    anyhow::anyhow!(
                        "Leg instrument {} not found in contract details after loading",
                        leg_instrument_id
                    )
                })?;

            leg_contract_details.push((leg_details, *ratio));
        }

        // Create the spread instrument
        let timestamp = nautilus_core::time::get_atomic_clock_realtime().get_time_ns();
        let leg_details_refs: Vec<(&ibapi::contracts::ContractDetails, i32)> =
            leg_contract_details.iter().map(|(d, r)| (d, *r)).collect();

        let bag_contract = self.create_bag_contract_from_legs(
            &leg_contract_details,
            Some(spread_instrument_id),
            None,
        )?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Load each leg instrument individually (load_ids_async per leg) and confirm the load succeeds before fetching the spread
  2. Verify the leg symbol, venue (exchange), and currency exactly match a tradable IB contract
  3. Check that the leg load path actually inserts into contract_details (look for early returns on empty IB results)
  4. Retry after the async load completes — the error can occur if the spread is fetched before leg loads finish

Example fix

// before
self.fetch_spread_instrument(instrument, leg_ids).await?;
// after
for leg in leg_ids {
    self.load_ids_async(&[leg.clone()], None).await?;
}
self.fetch_spread_instrument(instrument, leg_ids).await?;
Defensive patterns

Strategy: validation

Validate before calling

if provider.contract_details.get(&leg_id).is_none() {
    provider.load_ids_async(&[leg_id.clone()], None).await?;
}

Type guard

fn is_leg_cached(provider: &IbInstrumentsProvider, id: &InstrumentId) -> bool {
    provider.contract_details.get(id).is_some()
}

Try / catch

match fetch_spread_instrument(...).await {
    Ok(i) => Ok(i),
    Err(e) if e.to_string().contains("not found in contract details") =>
        Err(SpreadError::LegMissing(e.to_string())),
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Calling resolve_contract_for_instrument_async / load_with_return_async / load_ids_async for a spread whose leg instrument_id has no entry in self.contract_details after load — e.g. the leg load request returned no matching contract for that symbol/exchange/currency.

Common situations: Leg symbol misspelled or with wrong venue/currency so IB returns no details; leg never individually subscribed/loaded before the spread fetch; IB API returned an empty contract details list for the leg and the cache write was skipped.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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