nautechsystems/nautilus_trader · error
Cannot register OMS type: {e}
Error message
Cannot register OMS type: {e} What it means
This error wraps a `RefCell::try_borrow_mut` failure on the live node's kernel exec engine. The kernel stores `exec_engine` in a `RefCell` to allow shared access across the node; if the engine is already mutably (or even immutably) borrowed by another call on the same thread, `try_borrow_mut` returns `Err` and the node raises this message. It is a re-entrancy / aliasing violation, not a domain problem with the OMS type itself.
Source
Thrown at crates/live/src/node/mod.rs:2722
// Capture strategy-owned values before adding the strategy, which moves it
let strategy_id = self
.kernel
.trader
.borrow()
.prepare_strategy_for_registration(&mut strategy)?;
let oms_type = StrategyNative::strategy_core(&strategy).config.oms_type;
let instrument_ids = strategy.external_order_instrument_ids().unwrap_or_default();
if !instrument_ids.is_empty() {
self.register_external_order_claims(strategy_id, &instrument_ids)?;
}
let mut exec_engine = match oms_type
.map(|_| {
self.kernel
.exec_engine
.try_borrow_mut()
.map_err(|e| anyhow::anyhow!("Cannot register OMS type: {e}"))
})
.transpose()
{
Ok(exec_engine) => exec_engine,
Err(e) => {
if !instrument_ids.is_empty()
&& let Err(rollback_error) =
self.rollback_external_order_claims(strategy_id, &instrument_ids)
{
anyhow::bail!(
"{e}; failed to roll back external order claims for {strategy_id}: {rollback_error}"
);
}
return Err(e);
}
};
if let Err(add_error) = self.kernel.trader.borrow_mut().add_strategy(strategy) {View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure no other borrow of the kernel exec engine is alive on the current thread when calling `add_strategy` (drop `borrow()`/`borrow_mut()` guards first).
- Move strategy registration to node startup, before the trading loop/event callbacks begin.
- If calling from a callback, defer registration (queue it and register from the outer scope).
- Inspect the `e` in the message: Pyo3 borrows print the location of the conflicting borrow, which identifies the holder.
Example fix
// before let engine = node.kernel.exec_engine.borrow(); node.add_strategy(strategy, ...)?; // after drop(node.kernel.exec_engine.borrow()); // release before registering node.add_strategy(strategy, ...)?;
Defensive patterns
Strategy: try-catch
Validate before calling
// Check the exec engine borrow is free before registering
if node.kernel.exec_engine.try_borrow().is_err() {
return Err(anyhow::anyhow!("exec engine already borrowed; defer add_strategy"));
} Try / catch
match node.add_strategy(strategy, instrument_ids) {
Ok(()) => {},
Err(e) if e.to_string().contains("Cannot register OMS type") => {
// retry after borrows are released / defer to startup phase
}
Err(e) => return Err(e),
} Prevention
- Never hold `borrow()`/`borrow_mut()` guards on kernel fields across calls that register components
- Register strategies/actors during node setup, before the event loop runs
- Avoid calling add_strategy from inside event or data callbacks
- Read the wrapped Pyo3 borrow message to locate the conflicting borrow site
When it happens
Trigger: Calling `add_strategy` while the kernel's `exec_engine` RefCell is already borrowed — e.g. calling `add_strategy` from inside a callback that itself holds a borrow of the exec engine, or calling it while a previous registration on the same thread is still in scope.
Common situations: Registering a strategy from within an actor/event handler on the live node; nested `add_strategy` calls in async code where a previous borrow hasn't been dropped; holding `node.kernel.exec_engine.borrow()` in user code before calling `add_strategy`.
Related errors
- Cannot register external order claims: {e}
- Cannot deregister external order claims: {e}
- Cannot roll back external order claims: {e}
- Invalid NodeState value
- LiveNodeConfig.event_store is set but no factory was registe
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/85d1ef7adf9ed195.
Report an issue: GitHub.