{"record":{"id":"c06ae79f36027cb3","repo":"nautechsystems/nautilus_trader","slug":"python-on-degrade-failed-e-c06ae7","errorCode":null,"errorMessage":"Python on_degrade failed: {e}","messagePattern":"Python on_degrade failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/trading/src/python/strategy.rs","lineNumber":1086,"sourceCode":"\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<()> {\n        self.dispatch_on_degrade()\n            .map_err(|e| anyhow::anyhow!(\"Python on_degrade failed: {e}\"))\n    }\n\n    fn on_fault(&mut self) -> anyhow::Result<()> {\n        self.dispatch_on_fault()\n            .map_err(|e| anyhow::anyhow!(\"Python on_fault failed: {e}\"))\n    }\n\n    fn on_save(&self) -> anyhow::Result<IndexMap<String, Vec<u8>>> {\n        self.dispatch_on_save()\n            .map_err(|e| anyhow::anyhow!(\"Python on_save failed: {e}\"))\n    }\n\n    fn on_load(&mut self, state: IndexMap<String, Vec<u8>>) -> anyhow::Result<()> {\n        self.dispatch_on_load(&state)\n            .map_err(|e| anyhow::anyhow!(\"Python on_load failed: {e}\"))\n    }\n\n    fn on_time_event(&mut self, event: &TimeEvent) -> anyhow::Result<()> {","sourceCodeStart":1068,"sourceCodeEnd":1104,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/trading/src/python/strategy.rs#L1068-L1104","documentation":"The strategy's Python-level `on_degrade` callback raised an exception. The Rust `DataActor`/strategy wrapper calls `dispatch_on_degrade()` (crates/trading/src/python/strategy.rs:370), which invokes `on_degrade` on the Python instance via `call_method0`; any Python exception is converted to a `PyResult` error and re-wrapped with this anyhow message. The message text interpolates the underlying Python exception (`{e}`), so the actual cause is whatever traceback the user's `on_degrade` raised.","triggerScenarios":"The node transitions to a DEGRADED state and the framework calls `on_degrade()` on the strategy; the user-implemented Python `on_degrade` method raises any exception (e.g. referencing an attribute that is None because an upstream dependency degraded, or calling an API in an inconsistent state).","commonSituations":"Degradation handlers that try to close or reset connections to services that are already unreachable; `on_degrade` code assuming `self.cache`/`self.portfolio` state that is not present; typos or signature changes in the Python method; raising deliberately inside `on_degrade` to signal failure but expecting it to be swallowed.","solutions":["Read the interpolated `{e}` text for the underlying Python exception and fix the bug inside your Python `on_degrade` implementation.","Wrap the body of `on_degrade` in try/except, log, and perform defensive cleanup so degradation itself never raises.","Verify the strategy class actually defines `on_degrade` (not just inherits from a base class whose signature changed).","Guard against degraded dependencies (e.g. check client is not None) before using them in the handler."],"exampleFix":"// before (Python)\ndef on_degrade(self):\n    self.client.reset()  # raises if client is None\n\n// after\ndef on_degrade(self):\n    if self.client is None:\n        self.log.warning(\"client already unavailable; nothing to reset\")\n        return\n    try:\n        self.client.reset()\n    except Exception as e:\n        self.log.error(f\"degrade cleanup failed: {e}\")","handlingStrategy":"try-catch","validationCode":"def _check_degrade_safe(strategy):\n    import inspect\n    fn = getattr(strategy, \"on_degrade\", None)\n    return callable(fn) and len(inspect.signature(fn).parameters) == 1","typeGuard":"def has_hook(obj, name):\n    return callable(getattr(obj, name, None))","tryCatchPattern":"try:\n    self.on_degrade()\nexcept Exception as e:\n    self.log.error(f\"on_degrade raised: {e}\")  # never let lifecycle hooks escape","preventionTips":["Keep lifecycle hooks free of external/network calls, or wrap every call in try/except with logging.","Initialize all attributes used by hooks in __init__ so they exist even on early failures.","Smoke-test degrade/fault/stop transitions in CI, not just start paths.","Never intentionally raise from on_degrade; return/log instead."],"tags":["python","strategy","lifecycle-hook","callback"],"backgroundTag":"python-callback-failed","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"}