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_idView on GitHub (pinned to 18893faf8b)
Solutions
- Inspect debug logs for per-leg failures; fix the underlying leg resolution errors (bad con_ids, missing permissions).
- Validate each leg's con_id is live on IB (check in TWS).
- Re-run after confirming IB Gateway connectivity and market-data permissions for each leg's exchange.
- 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
- Check leg con_ids are live in TWS before building spreads
- Review debug logs for per-leg failures — a single silent leg failure can empty the whole set
- Replace legs when contracts expire
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
- Cannot resolve BAG contract without combo legs or cached con
- Resolved BAG spread {spread_instrument_id} is not cached
- Invalid BAG contract: must have security_type=Spread and non
- Cannot create BAG contract without leg details
- Leg instrument {} not found in contract details after loadin
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/47dd217ece6cbbfd.
Report an issue: GitHub.