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
- Ensure all traded instruments are requested/loaded into the instruments map before subscribing to the private orders channel
- Subscribe to the instrument on this adapter instance so its definition is cached when order updates arrive
- Check the inst_id string matches exactly what the adapter stores (e.g. 'BTC-USDT-SWAP' vs 'BTC-USDT')
- 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
- Load all instruments you trade into the adapter before opening the private orders channel
- Re-sync the instrument cache on adapter restart while orders may still be open
- Keep inst_id strings verbatim from OKX (no reformatting/case changes)
- Reconcile open orders at startup and backfill any missing instrument definitions
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
- No instrument found for sprd_id: {}
- Unsupported algo order type: {:?}
- Missing sz for algo order {}
- Cannot determine spread fill quantity: fill_sz='{}' and acc_
- Cannot determine fill quantity: fill_sz is empty/zero and ac
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/6af0e56b817b10cf.
Report an issue: GitHub.