nautechsystems/nautilus_trader · error

No matching engine found for instrument {}

Error message

No matching engine found for instrument {}

What it means

The backtest exchange processes a data delta whose instrument is not present in the cache, so it cannot create (or find) a matching engine for that instrument. The exchange lazily adds a matching engine only when the instrument for the data's instrument_id exists in the cache; when cache.instrument() returns None it fails with this error. It prevents book/quote/trade data for unknown instruments from silently being dropped.

Source

Thrown at crates/backtest/src/exchange.rs:890

    /// Processes a single order book delta.
    ///
    /// # Errors
    ///
    /// Returns an error if module pre-processing or matching engine processing fails.
    pub fn process_order_book_delta(&mut self, delta: OrderBookDelta) -> anyhow::Result<()> {
        self.pre_process_modules(&Data::BookDelta(delta))?;

        if !self.matching_engines.contains_key(&delta.instrument_id) {
            let instrument = {
                let cache = self.cache.as_ref().borrow();
                cache.instrument(&delta.instrument_id).cloned()
            };

            if let Some(instrument) = instrument {
                self.add_instrument(instrument)?;
            } else {
                anyhow::bail!(
                    "No matching engine found for instrument {}",
                    delta.instrument_id
                );
            }
        }

        if let Some(matching_engine) = self.matching_engines.get_mut(&delta.instrument_id) {
            matching_engine.process_order_book_delta(&delta)?;
        } else {
            anyhow::bail!("Matching engine should be initialized");
        }
        Ok(())
    }

    /// Processes a batch of order book deltas.
    ///
    /// # Errors
    ///

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Load instrument definitions into the cache before feeding data (e.g. pass instruments via the BacktestEngine config add_instrument or ensure the data catalog/instrument provider loads them).
  2. Verify the instrument_id in the delta exactly matches a cached instrument (venue and symbol, case-sensitive).
  3. Log the missing instrument_id and compare against cache.instrument_ids() to find the mismatch.
  4. If the data legitimately has no instrument definition, filter those deltas out before passing them to the exchange.

Example fix

// before
exchange.process_order_book_delta(&delta)?; // delta for BTCUSDT.BINANCE, instrument never added

// after
engine.add_instrument(test_instrument_btcusdt_binance()); // or configure instrument provider
exchange.process_order_book_delta(&delta)?;
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_instrument_cached(cache: &Cache, instrument_id: &InstrumentId) -> anyhow::Result<()> {
    if cache.instrument(instrument_id).is_none() {
        anyhow::bail!("Instrument {instrument_id} not in cache; load definitions before feeding data");
    }
    Ok(())
}

Type guard

let Some(instrument) = cache.instrument(&delta.instrument_id) else {
    anyhow::bail!("Skipping delta for unknown instrument {}", delta.instrument_id);
};

Try / catch

match exchange.process_order_book_delta(&delta) {
    Ok(()) => {},
    Err(e) if e.to_string().contains("No matching engine found") => {
        log::warn!("skipping unregistered instrument delta: {e}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling SimulatedExchange process functions such as process_order_book_delta (exchange.rs:890) with an OrderBookDelta whose instrument_id was never added via add_instrument, and no instrument with that ID exists in the provided cache.

Common situations: Replaying recorded data where the instrument definitions file was not loaded first; instrument_id venue/symbol casing mismatch between data and instrument definitions; loading data for a venue that was not registered in the backtest config; renamed or delisted instruments in historical datasets.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — 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/9f5fd436fc855431. Report an issue: GitHub.