nautechsystems/nautilus_trader · error

Python on_reset failed: {e}

Error message

Python on_reset failed: {e}

What it means

This error wraps any Python exception raised by the strategy's user-implemented `on_reset()` callback. The Rust `DataActor::on_reset` dispatches to the Python instance via `call_method0(py, "on_reset")` (crates/trading/src/python/strategy.rs:356-361) and re-wraps any `PyErr` as `anyhow::anyhow!("Python on_reset failed: {e}")`. Reset is used to return the actor to a clean state (e.g. between backtest runs), so a failure here can poison subsequent runs with stale state.

Source

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

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

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

    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>>> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the chained Python traceback to find the raising line in `on_reset`.
  2. Initialize all resettable attributes in `__init__` (e.g. `self._orders = {}`) so reset can safely clear them.
  3. Use tolerant clears (`dict.pop`, `getattr` with defaults, `contextlib.suppress`) in `on_reset`.
  4. Make `on_reset` idempotent and test it in a start -> reset -> start cycle.

Example fix

// before (strategy.py)
def on_reset(self):
    self.cache.delete(self._cache_key)  # raises if never created

// after
def on_reset(self):
    self._orders = {}
    self.cache.delete(self._cache_key) if self.cache.check(self._cache_key) else None
Defensive patterns

Strategy: try-catch

Validate before calling

# before reset in a backtest loop
missing = [a for a in ("_orders", "_positions") if not hasattr(strategy, a)]
assert not missing, f"uninitialized resettable attrs: {missing}"

Type guard

def safely_reset(obj, attr, default):
    if hasattr(obj, attr):
        setattr(obj, attr, default)

Try / catch

def on_reset(self):
    with contextlib.suppress(Exception):
        self.cache.delete(self._cache_key)
    self._orders = {}
    self._last_event_ts = None

Prevention

When it happens

Trigger: Calling `strategy.reset()` (engine reset, backtest re-run, or post-fault recovery) when the Python subclass's `on_reset()` raises: e.g. clearing state containers that were never initialized, releasing resources already released, or any bug in user reset logic.

Common situations: Backtest loops where the second run's `on_reset` hits state assumptions from the first run, deleting files or keys that don't exist, or resetting attributes that were never set because the strategy never started.

Related errors


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