{"record":{"id":"45d8cdbd751d0a0c","repo":"nautechsystems/nautilus_trader","slug":"python-on-stop-failed-e-45d8cd","errorCode":null,"errorMessage":"Python on_stop failed: {e}","messagePattern":"Python on_stop failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/trading/src/python/strategy.rs","lineNumber":1066,"sourceCode":"    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<()> {\n        self.dispatch_on_dispose()\n            .map_err(|e| anyhow::anyhow!(\"Python on_dispose failed: {e}\"))\n    }\n\n    fn on_degrade(&mut self) -> anyhow::Result<()> {","sourceCodeStart":1048,"sourceCodeEnd":1084,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/trading/src/python/strategy.rs#L1048-L1084","documentation":"This error wraps any Python exception raised by the strategy's user-implemented `on_stop()` callback. The Rust `DataActor::on_stop` dispatches to the Python instance via `call_method0(py, \"on_stop\")` (crates/trading/src/python/strategy.rs:342-347) and re-wraps any `PyErr` as `anyhow::anyhow!(\"Python on_stop failed: {e}\")`. It indicates cleanup code executed at strategy shutdown failed, which can leave orders, subscriptions, or timers in an unclean state.","triggerScenarios":"Calling `strategy.stop()` (or stopping the trader node / Ctrl+C shutdown) when the Python subclass's `on_stop()` raises: e.g. canceling orders through a disconnected client, accessing actors/cache state already torn down, or any bug in user cleanup code.","commonSituations":"Shutdown-time races (venue/adapter already disconnected when cancel-orders runs), NameError/AttributeError in cleanup logic added after initial development, or double-stop flows where state was already reset.","solutions":["Read the chained Python traceback to find the raising line in `on_stop`.","Guard each cleanup step in `on_stop` with `try/except` so one failing step doesn't abort the rest of shutdown.","Check client/adapter connectivity before calling cancel/close/unsubscribe operations in `on_stop`.","Make `on_stop` idempotent so repeated stop calls or already-released resources don't raise."],"exampleFix":"// before (strategy.py)\ndef on_stop(self):\n    self.cancel_all_orders(self.instrument.id)  # raises if client disconnected\n\n// after\ndef on_stop(self):\n    try:\n        self.cancel_all_orders(self.instrument.id)\n    except Exception as e:\n        self.log.error(f\"Failed to cancel orders on stop: {e}\")","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"def on_stop(self):\n    for step in (self._cancel_open_orders, self._unsubscribe_all):\n        try:\n            step()\n        except Exception as e:\n            self.log.exception(f\"on_stop step {step.__name__} failed: {e}\")","preventionTips":["Make every step of `on_stop` independently guarded so shutdown always completes.","Check connectivity before cancel/unsubscribe calls at shutdown time.","Write `on_stop` to be idempotent; assume it may run after a fault or repeated stop.","Test the full stop path (including Ctrl+C shutdown) in staging regularly."],"tags":["python","strategy","lifecycle","on-stop"],"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-14T05:17:10.506Z"}