{"record":{"id":"a6681791cf82ae80","repo":"nautechsystems/nautilus_trader","slug":"python-on-instrument-failed-e-a66817","errorCode":null,"errorMessage":"Python on_instrument failed: {e}","messagePattern":"Python on_instrument failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/trading/src/python/strategy.rs","lineNumber":1139,"sourceCode":"            .map_err(|e| anyhow::anyhow!(\"Python on_signal failed: {e}\"))\n    }\n\n    fn on_queue_state(&mut self, event: &QueueStateChanged) -> anyhow::Result<()> {\n        self.dispatch_on_queue_state(event)\n            .map_err(|e| anyhow::anyhow!(\"Python on_queue_state failed: {e}\"))\n    }\n\n    fn on_socket_state(&mut self, event: &SocketStateChanged) -> anyhow::Result<()> {\n        self.dispatch_on_socket_state(event)\n            .map_err(|e| anyhow::anyhow!(\"Python on_socket_state failed: {e}\"))\n    }\n\n    fn on_instrument(&mut self, instrument: &InstrumentAny) -> anyhow::Result<()> {\n        Python::attach(|py| {\n            let py_instrument = instrument_any_to_pyobject(py, instrument.clone())\n                .map_err(|e| anyhow::anyhow!(\"Failed to convert InstrumentAny to Python: {e}\"))?;\n            self.dispatch_on_instrument(py_instrument)\n                .map_err(|e| anyhow::anyhow!(\"Python on_instrument failed: {e}\"))\n        })\n    }\n\n    fn on_quote(&mut self, quote: &QuoteTick) -> anyhow::Result<()> {\n        self.dispatch_on_quote(*quote)\n            .map_err(|e| anyhow::anyhow!(\"Python on_quote failed: {e}\"))\n    }\n\n    fn on_trade(&mut self, tick: &TradeTick) -> anyhow::Result<()> {\n        self.dispatch_on_trade(*tick)\n            .map_err(|e| anyhow::anyhow!(\"Python on_trade failed: {e}\"))\n    }\n\n    fn on_bar(&mut self, bar: &Bar) -> anyhow::Result<()> {\n        self.dispatch_on_bar(*bar)\n            .map_err(|e| anyhow::anyhow!(\"Python on_bar failed: {e}\"))\n    }\n","sourceCodeStart":1121,"sourceCodeEnd":1157,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/trading/src/python/strategy.rs#L1121-L1157","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\ndef on_instrument(self, instrument):\n    lot_size = instrument.option_details.lot_size  # AttributeError for non-options\n\n# after\ndef on_instrument(self, instrument):\n    if isinstance(instrument, Option):\n        lot_size = instrument.option_details.lot_size\n    else:\n        lot_size = instrument.size_increment","handlingStrategy":"try-catch","validationCode":"# in Python, before relying on instrument attributes\ndef validate_instrument(instrument):\n    assert instrument is not None and instrument.id.value\n    assert instrument.size_increment and instrument.price_increment","typeGuard":"def is_option(instrument):\n    return hasattr(instrument, 'option_details') and instrument.option_details is not None","tryCatchPattern":"def on_instrument(self, instrument):\n    try:\n        self._handle_instrument(instrument)\n    except Exception as e:\n        self.log.error(f\"on_instrument failed for {instrument.id}: {e}\", exc_info=True)","preventionTips":["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"],"tags":["python","strategy","callback","rust-binding"],"backgroundTag":"python-strategy-callback-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}