nautechsystems/nautilus_trader · error · anyhow::Error

Resolved BAG spread {spread_instrument_id} is not cached

Error message

Resolved BAG spread {spread_instrument_id} is not cached

What it means

After deriving a spread instrument ID from a BAG contract's combo legs, the adapter requires that the resulting spread instrument already exist in the provider's instrument cache. If the legs resolved to a valid spread ID but no cached instrument matches it, this error prevents returning an ID for an instrument the system has no definition for.

Source

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

                .with_context(|| {
                    format!(
                        "Cannot resolve BAG leg con_id {} to cached instrument ID",
                        combo_leg.contract_id
                    )
                })?;
            let ratio = IbAction::from_str(combo_leg.action.as_str())
                .context("Invalid BAG combo leg action")?
                .signed_multiplier()
                * combo_leg.ratio;

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

        let spread_instrument_id = create_spread_instrument_id(&leg_tuples)
            .context("Failed to create spread instrument ID from BAG combo legs")?;

        if self.find(&spread_instrument_id).is_none() {
            anyhow::bail!("Resolved BAG spread {spread_instrument_id} is not cached");
        }

        Ok(spread_instrument_id)
    }

    /// Check if a security type should be filtered.
    ///
    /// # Arguments
    ///
    /// * `sec_type` - The security type to check
    ///
    /// # Returns
    ///
    /// Returns `true` if the security type should be filtered.
    #[must_use]
    pub fn is_filtered_sec_type(&self, sec_type: &str) -> bool {
        self.config
            .filter_sec_types

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the spread instrument was created/cached first — call get_instrument (or the loader) for the spread before resolving its ID.
  2. Check that the leg instruments composing the spread are themselves loaded and unchanged (leg changes alter the computed spread ID).
  3. Re-run instrument loading after adapter restarts so the cache is repopulated.
  4. If the spread is new, define it explicitly so its ID matches the combination of leg IDs and ratios.

Example fix

// before: resolving before the spread is defined
let id = provider.resolve_instrument_id_for_contract(&bag)?;
// after: define/load the spread first, then resolve
let instrument = provider.get_instrument(&client, &bag)?; // caches spread definition
let id = instrument.id();
Defensive patterns

Strategy: validation

Validate before calling

fn spread_is_cached(provider: &IbInstrumentsProvider, id: &InstrumentId) -> bool {
    provider.find(id).is_some()
}

Prevention

When it happens

Trigger: resolve_spread_instrument_id_for_contract computes create_spread_instrument_id from the legs, but find(&spread_instrument_id) returns None — i.e., the spread was never loaded/defined via get_instrument or load path beforehand.

Common situations: Requesting a spread whose leg instruments were never subscribed/loaded, legs changed so the computed ID no longer matches any cached definition, or the spread definition was created after the resolution attempt.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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