{"record":{"id":"72d9817f30ae0ded","repo":"nautechsystems/nautilus_trader","slug":"python-on-start-failed-e-72d981","errorCode":null,"errorMessage":"Python on_start failed: {e}","messagePattern":"Python on_start failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/trading/src/python/strategy.rs","lineNumber":1061,"sourceCode":"\n    fn on_position_event(&mut self, event: PositionEvent) {\n        let _ = self.dispatch_on_position_event(event);\n    }\n\n    fn on_position_changed(&mut self, event: PositionChanged) {\n        let _ = self.dispatch_on_position_changed(event);\n    }\n\n    fn on_position_closed(&mut self, event: PositionClosed) {\n        let _ = self.dispatch_on_position_closed(event);\n    }\n}\n\nimpl DataActor for PyStrategyInner {\n    fn on_start(&mut self) -> anyhow::Result<()> {\n        Strategy::on_start(self)?;\n        self.dispatch_on_start()\n            .map_err(|e| anyhow::anyhow!(\"Python on_start failed: {e}\"))\n    }\n\n    fn on_stop(&mut self) -> anyhow::Result<()> {\n        self.dispatch_on_stop()\n            .map_err(|e| anyhow::anyhow!(\"Python on_stop failed: {e}\"))\n    }\n\n    fn on_resume(&mut self) -> anyhow::Result<()> {\n        self.dispatch_on_resume()\n            .map_err(|e| anyhow::anyhow!(\"Python on_resume failed: {e}\"))\n    }\n\n    fn on_reset(&mut self) -> anyhow::Result<()> {\n        self.dispatch_on_reset()\n            .map_err(|e| anyhow::anyhow!(\"Python on_reset failed: {e}\"))\n    }\n\n    fn on_dispose(&mut self) -> anyhow::Result<()> {","sourceCodeStart":1043,"sourceCodeEnd":1079,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/trading/src/python/strategy.rs#L1043-L1079","documentation":"This error wraps any Python exception raised by the strategy's user-implemented `on_start()` callback. The Rust `DataActor::on_start` first runs the native `Strategy::on_start` state transition, then dispatches to the Python instance via `py_self.call_method0(py, \"on_start\")` (crates/trading/src/python/strategy.rs:335-340). If that Python call returns a `PyErr`, it is re-wrapped with `anyhow::anyhow!(\"Python on_start failed: {e}\")` and propagated, aborting the strategy's startup sequence.","triggerScenarios":"Calling `strategy.start()` (or the trader/engine's start path) when the Python subclass's `on_start()` raises: e.g. subscribing to an invalid instrument ID, `self.subscribe_...` on a disconnected client, accessing config fields that are None, an unregistered clock timer name, or any unhandled exception in user code inside `on_start`.","commonSituations":"Config mistakes (missing/typo'd instrument IDs or client IDs in strategy config), calling subscription or order APIs before connections are ready, referencing cache entries that don't exist yet, or a Python-side bug such as a NameError/AttributeError introduced in a strategy edit.","solutions":["Read the chained Python traceback in the error message; it names the exact line in your strategy's `on_start` that raised.","Wrap risky startup logic (subscriptions, config access) in `try/except` inside `on_start` and log/handle, or fail fast with a clear message.","Validate strategy config fields (instrument IDs, client IDs, bar/quote types) before starting the trader node.","Verify all adapters/clients the strategy subscribes through are connected before `trader.start()`."],"exampleFix":"// before (strategy.py)\ndef on_start(self):\n    self.subscribe_bars(self.config.bar_type)  # raises if bar_type is None\n\n// after\ndef on_start(self):\n    if self.config.bar_type is None:\n        self.log.error(\"bar_type not configured; not subscribing\")\n        return\n    self.subscribe_bars(BarType.from_str(self.config.bar_type))","handlingStrategy":"try-catch","validationCode":"# before starting the trader\nassert strategy.config.bar_type is not None, \"bar_type missing\"\nassert strategy.config.instrument_id is not None, \"instrument_id missing\"\nfor cid in [strategy.config.client_id]:\n    assert trader is not None, \"trader not built\"\n# optionally dry-run the subscription targets through the cache\nassert strategy.cache.instrument(strategy.config.instrument_id) is not None","typeGuard":"def ensure_bar_type(config) -> BarType:\n    bt = getattr(config, \"bar_type\", None)\n    if not isinstance(bt, (str, BarType)) or (isinstance(bt, str) and not bt):\n        raise ValueError(f\"config.bar_type invalid: {bt!r}\")\n    return BarType.from_str(bt) if isinstance(bt, str) else bt","tryCatchPattern":"def on_start(self):\n    try:\n        self.subscribe_bars(self.bar_type)\n    except Exception as e:\n        self.log.exception(f\"on_start subscription failed: {e}\")\n        # decide: return (degrade) or re-raise to stop startup deliberately","preventionTips":["Validate the strategy config (instrument IDs, bar types, client IDs) before constructing/starting the trader.","Wrap external calls in `on_start` (subscribe, cache reads) in try/except with logging.","Ensure adapters/clients are connected before calling trader.start().","Exercise `on_start` in an integration test against a sandbox/backtest engine before live use."],"tags":["python","strategy","lifecycle","on-start"],"backgroundTag":"python-callback-raised","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}