nautechsystems/nautilus_trader · error
Python on_stop failed: {e}
Error message
Python on_stop failed: {e} What it means
This error is raised by the Rust `DataActor` bridge for Python actors (PyDataActorInner) when the user's Python `on_stop()` callback raises an exception. The Rust layer catches the Python error via `dispatch_on_stop()` and re-wraps it in an `anyhow::Error` with this message so the engine can surface it during component stop. The original Python traceback is embedded in `{e}`.
Source
Thrown at crates/common/src/python/actor.rs:1025
if let Err(e) = algo.execute_exec_algorithm_command(command) {
log::error!("Error executing command on Python algorithm {actor_id}: {e}");
}
} else {
log::error!("Python execution algorithm {actor_id} not found in registry");
}
});
msgbus::register_any(endpoint.into(), handler);
}
impl DataActor for PyDataActorInner {
fn on_start(&mut self) -> anyhow::Result<()> {
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<()> {View on GitHub (pinned to 18893faf8b)
Solutions
- Read the embedded Python traceback in `{e}` to find the exact line in your on_stop that raised.
- Make on_stop defensive: wrap fallible cleanup (network calls, order cancels) in try/except and log instead of raising.
- Verify resources used in on_stop (clients, cache, connections) are still valid at stop time and not already disposed.
- Run the strategy in isolation and call on_stop manually under a debugger to reproduce the failure.
Example fix
// before (Python)
def on_stop(self):
self.client.close()
self.logger.info(self.last_price)
// after
def on_stop(self):
try:
self.client.close()
except Exception as e:
self.log.error(f"cleanup failed: {e}")
self.logger.info(getattr(self, "last_price", None)) Defensive patterns
Strategy: try-catch
Validate before calling
import inspect assert inspect.ismethod(actor.on_stop), "on_stop must be a defined method" # dry-run in a test harness: actor._log; call on_stop with stubbed clients before live stop
Type guard
def has_safe_on_stop(actor):
return callable(getattr(actor, 'on_stop', None)) Try / catch
def on_stop(self):
try:
... # cleanup logic
except Exception as e:
self.log.error(f"on_stop cleanup failed: {e}") Prevention
- Never let cleanup code raise: wrap every fallible call in on_stop with try/except.
- Do not acquire new resources in on_stop; only release.
- Keep a stop-path unit test that runs on_stop twice to prove idempotency.
- Assert dependencies (clients, connections) are still valid before using them.
When it happens
Trigger: Any exception raised inside a custom Python Actor/Strategy's `on_stop` method: calling unavailable clients or resources being torn down, accessing attributes deleted earlier in the stop sequence, or a typo raising AttributeError, etc.
Common situations: Strategies that flush state, close network connections, or cancel orders in on_stop and hit an already-closed resource; referencing a `self.cache` or client object that was released; typos in method names inside cleanup code.
Related errors
- Python on_resume failed: {e}
- Python on_reset failed: {e}
- Python on_dispose failed: {e}
- Python on_degrade failed: {e}
- Python on_fault failed: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/eca6cf3eaaa017c3.
Report an issue: GitHub.