nautechsystems/nautilus_trader · error · InstrumentLookupError

InstrumentLookupError::not_found(instrument_id)

Error message

InstrumentLookupError::not_found(instrument_id)

What it means

Raised by `lazy_load_instrument` after fetching instrument data from Derive when no returned instrument matches the requested instrument_id. It wraps `InstrumentLookupError::not_found`, meaning the exchange query succeeded but the specific instrument does not exist (or is not tradable/listed).

Source

Thrown at crates/adapters/derive/src/data.rs:569

        instruments: Arc<AtomicMap<InstrumentId, InstrumentAny>>,
        instrument_id: InstrumentId,
        include_expired: bool,
    ) -> anyhow::Result<()> {
        let currency = currency_from_instrument_id(&instrument_id)?;
        let definitions = fetch_instrument_definitions(&http_client, currency, include_expired)
            .await
            .with_context(|| format!("failed to lazy-load Derive instruments for {currency}"))?;
        let mut found = false;

        for instrument in parse_instrument_definitions(definitions) {
            if instrument.id() == instrument_id {
                found = true;
            }
            cache_instrument(&instruments, &instrument);
        }

        if !found {
            anyhow::bail!(InstrumentLookupError::not_found(instrument_id));
        }

        Ok(())
    }

    fn ws_handle(&self) -> DeriveWebSocketSubscriptionHandle {
        self.ws_client.subscription_handle()
    }

    fn subscription_lifecycle(&self) -> SubscriptionLifecycle {
        SubscriptionLifecycle {
            registry: Arc::clone(&self.channel_subscriptions),
            lock: Arc::clone(&self.subscription_lock),
            dispatch: SubscriptionDispatchState {
                active_book_delta_channels: Arc::clone(&self.active_book_delta_channels),
                active_book_depth10_channels: Arc::clone(&self.active_book_depth10_channels),
                active_ticker_channels: Arc::clone(&self.active_ticker_channels),
                active_quote_subs: Arc::clone(&self.active_quote_subs),

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the InstrumentId symbol exists and is active on Derive (check symbol format and case).
  2. Confirm the instrument is not expired/delisted; use a current instrument.
  3. Ensure configured currencies/instrument-type filters cover the requested instrument.
  4. Add the instrument explicitly via request_instruments and check the response before subscribing.

Example fix

null
Defensive patterns

Strategy: try-catch

Validate before calling

// verify instrument exists on Derive before lazy load
let exists = derive_rest_instrument_exists(&symbol, &currency)?;

Try / catch

if let Err(e) = lazy_load_instrument(instrument_id).await {
    log::warn!("{e}; skipping subscription for {instrument_id}");
    return Ok(()); // degrade gracefully instead of failing the subscribe
}

Prevention

When it happens

Trigger: Auto-loading (`auto_load_missing_instruments: true`) an InstrumentId during prepare_subscribe where lazy_load_instrument queries Derive for the instrument type/currency but the matching instrument is absent from the response.

Common situations: Requesting delisted or expired instruments; misspelled or wrong-format symbols; requesting an instrument whose underlying currency isn't covered by the query used; instruments on a different Derive sub-market.

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/06289b0c11d8305c. Report an issue: GitHub.