nautechsystems/nautilus_trader · error

Python on_fault failed: {e}

Error message

Python on_fault failed: {e}

What it means

The strategy's Python-level `on_fault` callback raised an exception. The Rust wrapper calls `dispatch_on_fault()` (crates/trading/src/python/strategy.rs:377) via `call_method0`; any exception escaping the Python method is wrapped as `anyhow::anyhow!("Python on_fault failed: {e}")` at strategy.rs:1091. The interpolated `{e}` contains the real Python traceback cause.

Source

Thrown at crates/trading/src/python/strategy.rs:1091

    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<()> {
        route_time_event(self, event);
        self.dispatch_on_time_event(event)
            .map_err(|e| anyhow::anyhow!("Python on_time_event failed: {e}"))
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the `{e}` payload for the underlying Python exception and fix the fault-handler code.
  2. Make `on_fault` defensive: never call out to network/external systems without try/except and logging.
  3. Ensure all attributes used in `on_fault` are initialized in `__init__` so the handler works even on early-life faults.
  4. Confirm the method signature is `def on_fault(self)` with no extra required parameters.

Example fix

// before
def on_fault(self):
    self.alerts.send("strategy faulted")  # raises when alert service is down

// after
def on_fault(self):
    try:
        self.alerts.send("strategy faulted")
    except Exception as e:
        self.log.error(f"could not send fault alert: {e}")
Defensive patterns

Strategy: try-catch

Validate before calling

import inspect
def _check_fault_hook(strategy):
    fn = getattr(strategy, "on_fault", 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_fault()
except Exception as e:
    self.log.error(f"on_fault handler itself failed: {e}")

Prevention

When it happens

Trigger: A FAULT state transition triggers the registered actor/strategy hook; the user's Python `on_fault` method raises (e.g. attempting to use resources that are themselves in a faulted state, or an unhandled error in fault reporting/alerting code).

Common situations: Fault handlers that try to publish notifications through a broken network client; `on_fault` referencing `self` state that was never initialized because the fault occurred during start-up; exception inside a `finally`-style cleanup racing with shutdown.

Related errors


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