nautechsystems/nautilus_trader · error · anyhow::Error

Cannot submit order {order.client_order_id()}: cache is curr

Error message

Cannot submit order {order.client_order_id()}: cache is currently borrowed

What it means

Raised in `submit_order_native` when the strategy cache is already mutably (or immutably) borrowed at the point the binding tries `try_borrow_mut`, so the new order cannot be added to the cache. This is a runtime borrow-check guard of the RefCell-based cache, not a domain error.

Source

Thrown at crates/trading/src/strategy/mod.rs:2359

    let market_exit_tag = core.market_exit_tag;
    let is_market_exit_order = order
        .tags()
        .is_some_and(|tags| tags.contains(&market_exit_tag));
    let should_deny_for_market_exit =
        core.is_exiting && !order.is_reduce_only() && !is_market_exit_order;

    if should_deny_for_market_exit {
        strategy.deny_order(order, Ustr::from("MARKET_EXIT_IN_PROGRESS"));
        return Ok(());
    }

    let core = StrategyNative::strategy_core_mut(strategy);
    let params = params.filter(|params| !params.is_empty());

    {
        let cache_rc = core.cache_rc();
        let mut cache = cache_rc.try_borrow_mut().map_err(|_| {
            anyhow::anyhow!(
                "Cannot submit order {}: cache is currently borrowed",
                order.client_order_id()
            )
        })?;
        cache.add_order(order.clone(), position_id, client_id, true)?;
    }

    publish_order_initialized(order);

    let command = SubmitOrder::new(
        trader_id,
        client_id,
        strategy_id,
        order.instrument_id(),
        order.client_order_id(),
        order.init_event().clone(),
        order.exec_algorithm_id(),
        position_id,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Do not submit orders from inside callbacks that hold a cache borrow; defer via a message queue or flag.
  2. Scope cache borrows tightly (drop them before calling back into strategy methods).
  3. Avoid holding the result of cache.borrow()/borrow_mut() across calls into strategy APIs.
  4. Restructure so submission happens after event handling completes (e.g. in the next timer tick).

Example fix

// before
fn on_order(&mut self, event: &OrderEventAny) {
    self.submit_order(new_order)?; // cache still borrowed here
}
// after
fn on_order(&mut self, event: &OrderEventAny) {
    self.pending_submits.push(new_order); // submit after borrow released
}
Defensive patterns

Strategy: try-catch

Validate before calling

// No pre-call check exists; ensure no live cache borrow on the calling path
// (avoid calling submit_order inside on_event/on_order callbacks that borrow cache)

Try / catch

if let Err(e) = strategy.submit_order(&order) {
    if e.to_string().contains("cache is currently borrowed") {
        deferred_submits.push(order); // retry after handler returns
    }
}

Prevention

When it happens

Trigger: Calling `submit_order` from a callback/re-entrant path (e.g. inside an event handler or from `binding_submit_order`) while another piece of code still holds a borrow of the strategy cache on the same thread.

Common situations: Submitting an order directly from within on_order/on_event/order-book callbacks that hold a cache borrow, or nested strategy calls where an earlier `cache.borrow()` was not released.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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