nautechsystems/nautilus_trader · critical

Matching engine not found for instrument {instrument_id}

Error message

Matching engine not found for instrument {instrument_id}

What it means

When processing a trading command (submit/modify/cancel/cancel-all) the exchange looked up the `MatchingEngine` for the command's instrument ID and, finding none, panicked. The engine map must contain an entry for every instrument whose orders are routed to this venue.

Source

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

                TradingCommand::ModifyOrder(ref command) => {
                    matching_engine.process_modify(command, account_id);
                }
                TradingCommand::ModifyOrders(ref command) => {
                    matching_engine.process_batch_modify(command, account_id);
                }
                TradingCommand::CancelOrder(ref command) => {
                    matching_engine.process_cancel(command, account_id);
                }
                TradingCommand::CancelOrders(ref command) => {
                    matching_engine.process_batch_cancel(command, account_id);
                }
                TradingCommand::CancelAllOrders(ref command) => {
                    matching_engine.process_cancel_all(command, account_id);
                }
                _ => {}
            }
        } else {
            panic!("Matching engine not found for instrument {instrument_id}");
        }
    }

    fn process_modify_submitted_order(&self, command: &ModifyOrder) -> bool {
        let Some(order) = self
            .cache
            .borrow()
            .order(&command.client_order_id)
            .map(|o| o.clone())
        else {
            return false;
        };

        let modifies_submitted_order = matches!(order.status(), OrderStatus::Submitted)
            || (matches!(order.status(), OrderStatus::PendingUpdate)
                && order
                    .previous_status()
                    .is_some_and(|status| matches!(status, OrderStatus::Submitted)));

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Register the instrument (matching engine) with the venue before routing commands to it.
  2. Filter or gate commands so only instruments registered with this exchange are routed to it.
  3. Log/instrument-coverage-check which instrument IDs the strategy uses versus venue-registered instruments.

Example fix

// before
engine.cancel_all_orders(instrument_id); // instrument never registered
// after
engine.add_venue(SimulatedExchangeConfig {
    instruments: vec![instrument],
    ..Default::default()
});
engine.cancel_all_orders(instrument_id);
Defensive patterns

Strategy: validation

Validate before calling

// before routing commands
if !venue.instrument_ids().contains(&instrument_id) {
    eprintln!("skipping command for unregistered instrument {instrument_id}");
    return;
}

Prevention

When it happens

Trigger: `TradingCommand::CancelAllOrders` (or any single-instrument trading command) targeting an `instrument_id` with no registered matching engine in `exchange.matching_engines`.

Common situations: Cancelling orders on an instrument that was never registered with the venue; commands broadcast to all instruments where some instruments lack engines; data/config mismatch after changing instrument definitions (e.g. different venue or expiration).

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