nautechsystems/nautilus_trader · error
Cannot roll back external order claims: {e}
Error message
Cannot roll back external order claims: {e} What it means
This error wraps a `RefCell::try_borrow_mut` failure on the kernel cache during `rollback_external_order_claims`, which reads the strategy's current claims, filters out the given instrument IDs, and rewrites the retained set. If the cache RefCell is already borrowed on the same thread, the borrow fails and this message is raised. It is called by `add_strategy` during rollback of a failed registration.
Source
Thrown at crates/live/src/node/mod.rs:2819
self.kernel
.cache
.try_borrow_mut()
.map_err(|e| anyhow::anyhow!("Cannot deregister external order claims: {e}"))?
.set_external_order_claims(strategy_id, &[])?;
Ok(())
}
pub(crate) fn rollback_external_order_claims(
&self,
strategy_id: StrategyId,
instrument_ids: &[InstrumentId],
) -> anyhow::Result<()> {
let mut cache = self
.kernel
.cache
.try_borrow_mut()
.map_err(|e| anyhow::anyhow!("Cannot roll back external order claims: {e}"))?;
let retained: Vec<_> = cache
.external_order_claim_instrument_ids(Some(strategy_id))
.into_iter()
.filter(|instrument_id| !instrument_ids.contains(instrument_id))
.collect();
cache.set_external_order_claims(strategy_id, &retained)
}
/// Adds an execution algorithm to the trader.
///
/// Execution algorithms are registered in both the component registry (for lifecycle
/// management) and the actor registry (for data callbacks via msgbus).
///
/// # Errors
///
/// Returns an error if:
/// - The node is currently running.
/// - An execution algorithm with the same ID is already registered.View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure no outstanding cache borrow exists when `add_strategy` (and thus its rollback) runs.
- Register strategies at setup time, before the node's event loop holds cache borrows.
- If this appears during rollback, look one level up: the original failure plus a live cache borrow; fix the outer borrow scope.
- Inspect the wrapped `e` for the conflicting borrow site.
Example fix
// before let cache = node.kernel.cache.borrow(); node.add_strategy(strategy, ...)?; // rollback path needs cache mutably // after drop(node.kernel.cache.borrow()); node.add_strategy(strategy, ...)?;
Defensive patterns
Strategy: try-catch
Validate before calling
if node.kernel.cache.try_borrow_mut().is_err() {
return Err(anyhow::anyhow!("cache already mutably borrowed; rollback will fail"));
} Try / catch
match node.add_strategy(strategy, ids) {
Err(e) if e.to_string().contains("Cannot roll back external order claims") => {
// fix outer cache borrow scope, then verify claims state via external_order_claim_instrument_ids
}
other => other?,
} Prevention
- Don't hold a cache borrow while calling add_strategy (its failure path needs the cache mutably)
- Fix the primary registration error first; a rollback borrow conflict usually indicates a nested borrow in the failure path
- Run strategy registration from a single-threaded setup phase
- Log claim state after rollback to confirm consistency
When it happens
Trigger: A failed `add_strategy` triggers rollback while the cache RefCell is still held by another borrow on the same thread — typically because the failure path runs inside code that itself borrowed the cache, or user code holds a cache borrow while adding strategies.
Common situations: Nested strategy registration failures during node startup; rolling back claims from a callback where the cache borrow is alive; concurrent (same-thread) node operations interleaved with strategy registration.
Related errors
- Cannot register OMS type: {e}
- Cannot register external order claims: {e}
- Cannot deregister external order claims: {e}
- Execution payload storage is in {operation} maintenance, not
- Execution payload rollback batch size must be positive
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f5e32c7ff1b10969.
Report an issue: GitHub.