nautechsystems/nautilus_trader · error
Python on_degrade failed: {e}
Error message
Python on_degrade failed: {e} What it means
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.
Source
Thrown at crates/trading/src/python/strategy.rs:1086
fn on_resume(&mut self) -> anyhow::Result<()> {
self.dispatch_on_resume()
.map_err(|e| anyhow::anyhow!("Python on_resume failed: {e}"))
}
fn on_reset(&mut self) -> anyhow::Result<()> {
self.dispatch_on_reset()
.map_err(|e| anyhow::anyhow!("Python on_reset failed: {e}"))
}
fn on_dispose(&mut self) -> anyhow::Result<()> {
self.dispatch_on_dispose()
.map_err(|e| anyhow::anyhow!("Python on_dispose failed: {e}"))
}
fn on_degrade(&mut self) -> anyhow::Result<()> {
self.dispatch_on_degrade()
.map_err(|e| anyhow::anyhow!("Python on_degrade failed: {e}"))
}
fn on_fault(&mut self) -> anyhow::Result<()> {
self.dispatch_on_fault()
.map_err(|e| anyhow::anyhow!("Python on_fault failed: {e}"))
}
fn on_save(&self) -> anyhow::Result<IndexMap<String, Vec<u8>>> {
self.dispatch_on_save()
.map_err(|e| anyhow::anyhow!("Python on_save failed: {e}"))
}
fn on_load(&mut self, state: IndexMap<String, Vec<u8>>) -> anyhow::Result<()> {
self.dispatch_on_load(&state)
.map_err(|e| anyhow::anyhow!("Python on_load failed: {e}"))
}
fn on_time_event(&mut self, event: &TimeEvent) -> anyhow::Result<()> {View on GitHub (pinned to 18893faf8b)
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.
Example fix
// before (Python)
def on_degrade(self):
self.client.reset() # raises if client is None
// after
def on_degrade(self):
if self.client is None:
self.log.warning("client already unavailable; nothing to reset")
return
try:
self.client.reset()
except Exception as e:
self.log.error(f"degrade cleanup failed: {e}") Defensive patterns
Strategy: try-catch
Validate before calling
def _check_degrade_safe(strategy):
import inspect
fn = getattr(strategy, "on_degrade", None)
return callable(fn) and len(inspect.signature(fn).parameters) == 1 Type guard
def has_hook(obj, name):
return callable(getattr(obj, name, None)) Try / catch
try:
self.on_degrade()
except Exception as e:
self.log.error(f"on_degrade raised: {e}") # never let lifecycle hooks escape Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Python on_fault failed: {e}
- Python on_time_event failed: {e}
- Python on_data failed: {e}
- Python on_signal failed: {e}
- Python on_instrument failed: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/c06ae79f36027cb3.
Report an issue: GitHub.