nautechsystems/nautilus_trader · error
Python on_degrade failed: {e}
Error message
Python on_degrade failed: {e} What it means
Raised by the Rust `DataActor` bridge when the Python actor's `on_degrade()` callback raises an exception. The `dispatch_on_degrade()` call forwards into Python and the exception is rewrapped in this anyhow error. Degrade is invoked when the component enters a degraded (reduced-functionality) operational state.
Source
Thrown at crates/common/src/python/actor.rs:1045
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
- Extract the Python traceback from the error message and fix the raising line in on_degrade.
- Wrap degrade actions in try/except so partial degradation still completes and logs failures.
- Verify every object on_degrade touches exists in both normal and degraded startup paths.
- Simulate a degrade event in a test to exercise the callback before going live.
Example fix
// before (Python)
def on_degrade(self):
self.unsubscribe(self.bar_type)
// after
def on_degrade(self):
try:
self.unsubscribe(self.bar_type)
except Exception as e:
self.log.warning(f"degrade unsubscribe failed: {e}") Defensive patterns
Strategy: try-catch
Validate before calling
# check degradation preconditions assert getattr(self, 'subscriptions', None) is not None, 'subscriptions must exist before degrade'
Type guard
def can_degrade(actor):
return callable(getattr(actor, 'on_degrade', None)) and getattr(actor, 'is_running', False) Try / catch
def on_degrade(self):
try:
... # reduce functionality
except Exception as e:
self.log.warning(f"on_degrade partial failure: {e}") Prevention
- Treat degrade as best-effort: log failures, never raise.
- Test degrade under staging conditions (feed outages) before live use.
- Verify every object touched in on_degrade exists in all startup paths.
- Keep degrade actions independent so one failure doesn't abort the rest.
When it happens
Trigger: Any exception thrown in the user's Python `on_degrade`, e.g. canceling data subscriptions or reducing exposure and hitting a failed call, None attribute, or bad argument inside the callback.
Common situations: Live trading venue degradation where on_degrade unsubscribes feeds and the unsubscribe path raises; strategies that downgrade indicators but reference an indicator that was never registered.
Related errors
- Python on_stop failed: {e}
- Python on_resume failed: {e}
- Python on_reset failed: {e}
- Python on_dispose failed: {e}
- Python on_fault failed: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/241a1e1857a0a7fc.
Report an issue: GitHub.