nautechsystems/nautilus_trader · error
Python on_instrument failed: {e}
Error message
Python on_instrument failed: {e} What it means
This error wraps a Python exception raised inside a strategy's `on_instrument` handler. The Rust core converted the `InstrumentAny` object to a Python object successfully, then called `dispatch_on_instrument`, which invokes the user-overridden Python method; any exception propagating out of that Python code is captured and re-thrown as this anyhow error. The library throws it so Rust-side infrastructure can uniformly handle (usually log or stop) strategy callback failures.
Source
Thrown at crates/trading/src/python/strategy.rs:1139
.map_err(|e| anyhow::anyhow!("Python on_signal failed: {e}"))
}
fn on_queue_state(&mut self, event: &QueueStateChanged) -> anyhow::Result<()> {
self.dispatch_on_queue_state(event)
.map_err(|e| anyhow::anyhow!("Python on_queue_state failed: {e}"))
}
fn on_socket_state(&mut self, event: &SocketStateChanged) -> anyhow::Result<()> {
self.dispatch_on_socket_state(event)
.map_err(|e| anyhow::anyhow!("Python on_socket_state failed: {e}"))
}
fn on_instrument(&mut self, instrument: &InstrumentAny) -> anyhow::Result<()> {
Python::attach(|py| {
let py_instrument = instrument_any_to_pyobject(py, instrument.clone())
.map_err(|e| anyhow::anyhow!("Failed to convert InstrumentAny to Python: {e}"))?;
self.dispatch_on_instrument(py_instrument)
.map_err(|e| anyhow::anyhow!("Python on_instrument failed: {e}"))
})
}
fn on_quote(&mut self, quote: &QuoteTick) -> anyhow::Result<()> {
self.dispatch_on_quote(*quote)
.map_err(|e| anyhow::anyhow!("Python on_quote failed: {e}"))
}
fn on_trade(&mut self, tick: &TradeTick) -> anyhow::Result<()> {
self.dispatch_on_trade(*tick)
.map_err(|e| anyhow::anyhow!("Python on_trade failed: {e}"))
}
fn on_bar(&mut self, bar: &Bar) -> anyhow::Result<()> {
self.dispatch_on_bar(*bar)
.map_err(|e| anyhow::anyhow!("Python on_bar failed: {e}"))
}
View on GitHub (pinned to 18893faf8b)
Solutions
- Read the chained `{e}` text in the log for the underlying Python traceback (file/line of the user handler) and fix the exception there.
- Wrap the body of the Python `on_instrument` override in try/except and log/handle expected per-instrument-type mismatches.
- Check the instrument's variant before using variant-specific attributes (e.g. `isinstance(instrument, CryptoPerpetual)`).
- Verify the strategy code against the installed nautilus_trader version for renamed instrument properties.
Example fix
# before
def on_instrument(self, instrument):
lot_size = instrument.option_details.lot_size # AttributeError for non-options
# after
def on_instrument(self, instrument):
if isinstance(instrument, Option):
lot_size = instrument.option_details.lot_size
else:
lot_size = instrument.size_increment Defensive patterns
Strategy: try-catch
Validate before calling
# in Python, before relying on instrument attributes
def validate_instrument(instrument):
assert instrument is not None and instrument.id.value
assert instrument.size_increment and instrument.price_increment Type guard
def is_option(instrument):
return hasattr(instrument, 'option_details') and instrument.option_details is not None Try / catch
def on_instrument(self, instrument):
try:
self._handle_instrument(instrument)
except Exception as e:
self.log.error(f"on_instrument failed for {instrument.id}: {e}", exc_info=True) Prevention
- Never assume variant-specific instrument attributes; isinstance-check first
- Handle every instrument type the subscription can deliver
- Log unexpected instruments instead of raising
- Cover each asset class with a unit test that feeds a representative instrument
When it happens
Trigger: Any unhandled Python exception in a strategy's `on_instrument(self, instrument)` override: e.g. calling an instrument attribute that is None for that asset class, dividing by an instrument's size_increment of 0, type errors from assuming a specific InstrumentAny variant, or an explicit `raise` in user code.
Common situations: Subscribing to instruments across multiple asset classes and assuming `instrument.option_details` or similar exists; copy-pasted handler logic from `on_bar` that indexes the instrument like a bar; library version change where an instrument property was renamed or made optional.
Related errors
- Python on_signal failed: {e}
- Python on_degrade failed: {e}
- Python on_time_event failed: {e}
- Python on_data failed: {e}
- Python on_signal failed: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/a6681791cf82ae80.
Report an issue: GitHub.