nautechsystems/nautilus_trader · error · anyhow::Error

Client order ID not found in cache

Error message

Client order ID not found in cache

What it means

`cancel_order` looks up the dYdX u32 client ID encoded for the Nautilus client_order_id via `self.encoder.get(...)`. If no encoding exists in the local cache, cancellation cannot build the on-chain cancel message and bails.

Source

Thrown at crates/adapters/dydx/src/execution/mod.rs:1956

        }; // Cache borrow released here

        log::debug!("Cancelling order {client_order_id} for instrument {instrument_id}");

        let (tx_manager, broadcaster, order_builder) = match self.get_execution_components() {
            Ok(components) => components,
            Err(e) => {
                log::error!("Failed to get execution components for cancel: {e}");
                return Ok(());
            }
        };

        let block_height = self.block_time_monitor.current_block_height() as u32;

        let encoded = match self.encoder.get(&client_order_id) {
            Some(enc) => enc,
            None => {
                log::error!("Client order ID {client_order_id} not found in cache");
                anyhow::bail!("Client order ID not found in cache")
            }
        };
        let client_id_u32 = encoded.client_id;

        log::debug!(
            "[CANCEL_ORDER] Nautilus '{client_order_id}' -> dYdX u32={client_id_u32} | instrument={instrument_id}"
        );

        // Stored flags remain authoritative after the order expires
        let order_flags = self.get_order_context(client_id_u32).map_or_else(
            || {
                log::warn!(
                    "Order context not found for {client_order_id}, deriving flags from order"
                );
                types::OrderLifetime::from_time_in_force(
                    order_time_in_force, // Using extracted value
                    order_expire_time,   // Using extracted value
                    false,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Only cancel orders submitted through the same client instance/session
  2. Persist and reload the client-ID encoder cache across restarts if supported
  3. Reconcile open orders on connect and re-register their IDs before canceling
  4. If the order is foreign, cancel it via venue_order_id-based paths or the dYdX API directly

Example fix

// before: cancel regardless of origin
client.cancel_order(cmd)?;
// after: skip orders not submitted this session
if encoder.contains(cmd.client_order_id) {
    client.cancel_order(cmd)?;
} else {
    log::warn!("{} not owned by this session; skipping", cmd.client_order_id);
}
Defensive patterns

Strategy: validation

Validate before calling

if !encoder.contains(client_order_id) { return Err(anyhow!("order not owned by this session")); }

Type guard

fn is_session_order(encoder: &Encoder, id: &ClientOrderId) -> bool { encoder.get(id).is_some() }

Try / catch

match client.cancel_order(cmd) {
    Err(e) if e.to_string().contains("not found in cache") => log::warn!("foreign/unknown order, skipping cancel"),
    r => r?,
}

Prevention

When it happens

Trigger: Canceling an order whose client_order_id was never submitted through this client instance (e.g. after a process restart with a cold cache, or an order placed by another client/session).

Common situations: Adapter restart with an empty encoder cache while the strategy still holds references to prior-session orders; reconciled venue orders not originated locally; submitting orders with a different adapter instance than the one canceling.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/560f5610b992463c. Report an issue: GitHub.