nautechsystems/nautilus_trader · error · anyhow::Error

Either instrument_ids or contracts must be provided

Error message

Either instrument_ids or contracts must be provided

What it means

request_instruments requires either Nautilus InstrumentIds or IB Contracts to know which instruments to load. If both lists are empty after unwrap_or_default, there is nothing to resolve and the client bails.

Source

Thrown at crates/adapters/interactive_brokers/src/historical/client.rs:849

    /// * `contracts` - Optional list of IB contracts
    ///
    /// # Returns
    ///
    /// Returns a list of instruments.
    ///
    /// # Errors
    ///
    /// Returns an error if loading fails.
    pub async fn request_instruments(
        &self,
        instrument_ids: Option<Vec<InstrumentId>>,
        contracts: Option<Vec<Contract>>,
    ) -> anyhow::Result<Vec<InstrumentAny>> {
        let instrument_ids = instrument_ids.unwrap_or_default();
        let contracts = contracts.unwrap_or_default();

        if instrument_ids.is_empty() && contracts.is_empty() {
            anyhow::bail!("Either instrument_ids or contracts must be provided");
        }

        let loaded_ids = self
            .instrument_provider
            .load_ids_with_return_async(&self.ib_client, instrument_ids, None)
            .await?;
        let mut loaded_instruments = self.instrument_provider.find_all(&loaded_ids);

        // Load instruments from contracts (equivalent to Python's _fetch_instruments_if_not_cached)
        for contract in contracts {
            match self
                .instrument_provider
                .get_instrument(&self.ib_client, &contract)
                .await
            {
                Ok(Some(instrument)) => {
                    if !loaded_instruments.iter().any(|i| i.id() == instrument.id()) {
                        loaded_instruments.push(instrument);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Pass at least one InstrumentId in instrument_ids.
  2. Or pass a non-empty Vec<Contract> when you already hold IB contract definitions.
  3. Populate the instrument universe config/subscription that produced the empty list.

Example fix

// before
client.request_instruments(None, None).await?;
// after
let ids = vec![InstrumentId::from("ESZ6.CME")];
client.request_instruments(Some(ids), None).await?;
Defensive patterns

Strategy: validation

Validate before calling

if instrument_ids.as_ref().map_or(true, |i| i.is_empty())
    && contracts.as_ref().map_or(true, |c| c.is_empty()) {
    return Err("request_instruments needs ids or contracts");
}

Prevention

When it happens

Trigger: Calling request_instruments with instrument_ids=None/[] and contracts=None/[].

Common situations: Passing None for both when intending 'load everything' (not supported); empty config-driven instrument universe; wrappers that filter ids before calling and end up with none.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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