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

  1. Extract the Python traceback from the error message and fix the raising line in on_degrade.
  2. Wrap degrade actions in try/except so partial degradation still completes and logs failures.
  3. Verify every object on_degrade touches exists in both normal and degraded startup paths.
  4. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/241a1e1857a0a7fc. Report an issue: GitHub.