nautechsystems/nautilus_trader · error
Cannot clear external order claims: {e}
Error message
Cannot clear external order claims: {e} What it means
Raised in `retire_strategy` when the cache `Rc<RefCell<Cache>>` cannot be mutably borrowed while attempting to clear external order claims for the retiring strategy. A live mutable borrow elsewhere (RefCell already borrowed) prevents the check, and the strategy would risk being disposed while still tracked.
Source
Thrown at crates/system/src/trader.rs:1459
/// through a bulk operation leaves the trader's bookkeeping consistent with the registries.
fn retire_actor(&mut self, actor_id: ActorId) -> anyhow::Result<()> {
Self::dispose_registered_component(actor_id.inner())?;
self.release_component(ComponentId::from(actor_id));
self.actor_ids.retain(|id| id != &actor_id);
self.actor_state_callbacks.remove(&actor_id);
Ok(())
}
/// Disposes a strategy, then releases everything its registration created.
fn retire_strategy(&mut self, strategy_id: StrategyId) -> anyhow::Result<()> {
// Check before disposal so a borrow failure cannot leave a disposed strategy registered.
{
let _cache = self
.cache
.try_borrow_mut()
.map_err(|e| anyhow::anyhow!("Cannot clear external order claims: {e}"))?;
}
Self::dispose_registered_component(strategy_id.inner())?;
self.cache
.try_borrow_mut()
.map_err(|e| anyhow::anyhow!("Cannot clear external order claims: {e}"))?
.set_external_order_claims(strategy_id, &[])?;
self.remove_strategy_subscriptions(strategy_id);
self.release_component(ComponentId::from(strategy_id));
self.strategy_ids.retain(|id| id != &strategy_id);
self.strategy_state_callbacks.remove(&strategy_id);
self.strategy_stop_fns.remove(&strategy_id);
Ok(())
}
View on GitHub (pinned to 18893faf8b)
Solutions
- Avoid calling trader retirement/removal APIs from inside code that holds a cache borrow (e.g. inside strategy handlers)
- Defer retirement to outside the current callback (queue it and execute after the handler returns)
- Inspect the chained BorrowError context to find who holds the borrow
- Refactor to use the trader's message bus/command pattern instead of direct nested calls
Example fix
// before: retiring from inside an on_event handler that borrows cache
def on_event(self, event):
self.trader.remove_strategy(self.id)
// after: defer via message/command
def on_event(self, event):
self.msgbus.send("trader.remove_strategy", self.id) # executed outside the borrow Defensive patterns
Strategy: try-catch
Try / catch
try:
trader.remove_strategy(strategy_id)
except Exception as e:
if "Cannot clear external order claims" in str(e):
logging.error("cache is borrowed elsewhere; defer removal: %s", e)
raise Prevention
- Never call trader removal/lifecycle APIs from inside strategy or actor callbacks that hold the cache
- Use the message bus to defer commands out of the current borrow scope
- Keep cache access short-lived and non-nested
When it happens
Trigger: Calling `remove_strategy`, `clear_strategies`, or `dispose_components` while another component (actor, strategy, or callback on the same thread) holds a mutable borrow of the cache, causing `try_borrow_mut` to fail with a BorrowError.
Common situations: Reentrant calls into the trader from within a strategy/actor callback that itself holds the cache borrow; nested strategy disposal while iterating cache state; calling trader lifecycle methods from inside event handlers.
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
- load_strategy not implemented for PostgreSQL cache adapter:
- OrderList denied: duplicate {}
- Order in list denied: duplicate {}
- Failed to load strategy {strategy_id} state: {e:#}
- Cannot set external order claims: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/e993bc30fb8ee074.
Report an issue: GitHub.