nautechsystems/nautilus_trader · error · anyhow::Error

No instrument found for inst_id: {}

Error message

No instrument found for inst_id: {}

What it means

parse_order_msg maps an OKX order message's inst_id to a previously cached local instrument; if the ID is absent from the instruments map, the message cannot be translated into an ExecutionReport and this error is thrown. The library requires the instrument to be subscribed/loaded before private order stream messages are parsed.

Source

Thrown at crates/adapters/okx/src/websocket/parse.rs:1358

}

/// Parses a single OKX order message into an [`ExecutionReport`].
///
/// # Errors
///
/// Returns an error if the instrument cannot be found or if parsing the
/// underlying order payload fails.
pub fn parse_order_msg(
    msg: &OKXOrderMsg,
    account_id: AccountId,
    instruments: &AHashMap<Ustr, InstrumentAny>,
    fee_cache: &AHashMap<Ustr, Money>,
    filled_qty_cache: &AHashMap<Ustr, Quantity>,
    ts_init: UnixNanos,
) -> anyhow::Result<ExecutionReport> {
    let instrument = instruments
        .get(&msg.inst_id)
        .ok_or_else(|| anyhow::anyhow!("No instrument found for inst_id: {}", msg.inst_id))?;

    let previous_fee = fee_cache.get(&msg.ord_id).copied();
    let previous_filled_qty = filled_qty_cache.get(&msg.ord_id).copied();

    let has_new_fill = (!msg.fill_sz.is_empty() && msg.fill_sz != "0")
        || !msg.trade_id.is_empty()
        || has_acc_fill_sz_increased(
            &msg.acc_fill_sz,
            previous_filled_qty,
            instrument.size_precision(),
        );

    warn_unrecognized_order_state(msg);

    match msg.state {
        OKXOrderStatus::Filled | OKXOrderStatus::PartiallyFilled | OKXOrderStatus::Unknown
            if has_new_fill =>
        {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure all traded instruments are requested/loaded into the instruments map before subscribing to the private orders channel
  2. Subscribe to the instrument on this adapter instance so its definition is cached when order updates arrive
  3. Check the inst_id string matches exactly what the adapter stores (e.g. 'BTC-USDT-SWAP' vs 'BTC-USDT')
  4. Add a lookup-and-fetch fallback: on miss, fetch the instrument via OKX HTTP API and insert into the map, then retry parsing

Example fix

// before
let instrument = instruments
    .get(&msg.inst_id)
    .ok_or_else(|| anyhow::anyhow!("No instrument found for inst_id: {}", msg.inst_id))?;
// after
let instrument = match instruments.get(&msg.inst_id) {
    Some(inst) => inst,
    None => {
        log::warn("Skipping order update for unknown instrument {inst_id}; ensure it is loaded/subscribed");
        return Ok(None);
    }
};
Defensive patterns

Strategy: validation

Validate before calling

// Rust, before processing private order updates
if !instruments.contains_key(&msg.inst_id) {
    log::warn("No cached instrument for {inst_id}; loading it before parse");
    load_instrument(msg.inst_id)?; // fetch via HTTP and insert into map
}

Type guard

fn instrument_cached(map: &AHashMap<Ustr, InstrumentAny>, inst_id: &Ustr) -> Option<InstrumentAny> {
    map.get(inst_id).copied()
}

Try / catch

match parse_order_msg(&msg, &instruments, account_id, &fee_cache, &filled_qty_cache, ts_init) {
    Ok(report) => dispatch(report),
    Err(e) if e.to_string().starts_with("No instrument found for inst_id") => {
        log::warn("{e}; ensure instrument is loaded before private stream messages");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: An OKX private 'orders' WebSocket message arrives for an instrument that was never loaded into the instruments AHashMap (no subscription, no HTTP instrument load, or cache cleared), so instruments.get(&msg.inst_id) returns None.

Common situations: Subscribing to the private orders channel without instruments loaded; orders placed manually on OKX's web/app UI for instruments not subscribed via the adapter; inst_id format mismatch (e.g. SPOT vs SWAP suffix variants); adapter restart wiping the cache while open orders still stream in.

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