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
- Inspect the `{e}` payload for the underlying Python exception and fix the fault-handler code.
- Make `on_fault` defensive: never call out to network/external systems without try/except and logging.
- Ensure all attributes used in `on_fault` are initialized in `__init__` so the handler works even on early-life faults.
- 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
- Treat on_fault as best-effort: never raise, only log and record.
- Avoid using faulted subsystems (network clients) inside the fault handler without guards.
- Add a unit test that calls on_fault on a freshly constructed strategy (pre-start state).
- Keep the method signature exactly def on_fault(self).
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
- Python on_degrade failed: {e}
- Invalid `strategy_id` type
- Failed to extract PyStrategy: {e}
- Failed to extract PyStrategy: {e}
- Failed to register PyStrategy: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/c746cd7d0bfeadf6.
Report an issue: GitHub.