nautechsystems/nautilus_trader · error

Python on_degrade failed: {e}

Error message

Python on_degrade failed: {e}

What it means

PyExecutionAlgorithm's DataActor on_degrade dispatches the Python `on_degrade` callback; any Python exception is re-wrapped with this anyhow message. Degrade is invoked when the system enters a degraded (reduced-capability) state, so a failure here means the Python degraded-mode handler raised.

Source

Thrown at crates/trading/src/python/algorithm.rs:575

        ExecutionAlgorithm::on_resume(self)?;
        self.dispatch_no_args("on_resume")
            .map_err(|e| anyhow::anyhow!("Python on_resume failed: {e}"))
    }

    fn on_reset(&mut self) -> anyhow::Result<()> {
        ExecutionAlgorithm::on_reset(self)?;
        self.dispatch_no_args("on_reset")
            .map_err(|e| anyhow::anyhow!("Python on_reset failed: {e}"))
    }

    fn on_dispose(&mut self) -> anyhow::Result<()> {
        self.dispatch_no_args("on_dispose")
            .map_err(|e| anyhow::anyhow!("Python on_dispose failed: {e}"))
    }

    fn on_degrade(&mut self) -> anyhow::Result<()> {
        self.dispatch_no_args("on_degrade")
            .map_err(|e| anyhow::anyhow!("Python on_degrade failed: {e}"))
    }

    fn on_fault(&mut self) -> anyhow::Result<()> {
        self.dispatch_no_args("on_fault")
            .map_err(|e| anyhow::anyhow!("Python on_fault failed: {e}"))
    }

    fn on_time_event(&mut self, event: &TimeEvent) -> anyhow::Result<()> {
        ExecutionAlgorithm::on_time_event(self, event)?;
        self.dispatch_time_event(event)
            .map_err(|e| anyhow::anyhow!("Python on_time_event failed: {e}"))
    }

    fn on_signal(&mut self, signal: &Signal) -> anyhow::Result<()> {
        self.dispatch_on_signal(signal)
            .map_err(|e| anyhow::anyhow!("Python on_signal failed: {e}"))
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the chained `{e}` traceback and fix the Python on_degrade implementation.
  2. Make on_degrade not depend on the resources whose failure caused the degradation.
  3. Guard state access so degrade works even if called before/after other lifecycle stages.
  4. Verify the zero-arg `def on_degrade(self)` signature.

Example fix

// before
def on_degrade(self):
    self.conn.pause()  # conn already dead

// after
def on_degrade(self):
    self.degraded = True  # local flag only; reconnect on_resume
Defensive patterns

Strategy: try-catch

Validate before calling

# Python: degrade must not depend on live connections
assert hasattr(self, "degraded"), "degrade flag not initialized in __init__"

Try / catch

if let Err(e) = actor.on_degrade() {
    log::error!("python on_degrade failed: {e:#}");
    // treat as fault if the actor cannot even enter degraded mode
}

Prevention

When it happens

Trigger: Calling on_degrade when the Python on_degrade raises — e.g. the callback assumes resources that are already unavailable, or wrong no-arg signature.

Common situations: Degrade triggered by connection loss where the handler then tries to use that same dead connection; typo'd or mis-signed degrade hook; degrade fired before on_start initialized state.

Related errors


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