nautechsystems/nautilus_trader · error
Python on_stop failed: {e}
Error message
Python on_stop failed: {e} What it means
The DataActor on_stop path first runs Rust ExecutionAlgorithm::on_stop, then dispatches Python `on_stop`; a Python exception is re-wrapped with this message. It means the Python component failed during orderly shutdown.
Source
Thrown at crates/trading/src/python/algorithm.rs:553
.dispatch_position_event("on_position_closed", PositionEvent::PositionClosed(event));
}
fn on_position_event(&mut self, event: PositionEvent) {
let _ = self.dispatch_position_event("on_position_event", event);
}
}
impl DataActor for PyExecutionAlgorithm {
fn on_start(&mut self) -> anyhow::Result<()> {
ExecutionAlgorithm::on_start(self)?;
self.dispatch_no_args("on_start")
.map_err(|e| anyhow::anyhow!("Python on_start failed: {e}"))
}
fn on_stop(&mut self) -> anyhow::Result<()> {
ExecutionAlgorithm::on_stop(self)?;
self.dispatch_no_args("on_stop")
.map_err(|e| anyhow::anyhow!("Python on_stop failed: {e}"))
}
fn on_resume(&mut self) -> anyhow::Result<()> {
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}"))
}View on GitHub (pinned to 18893faf8b)
Solutions
- Read the chained `{e}` traceback and fix the Python on_stop implementation.
- Make on_stop idempotent and defensive (guard already-closed/already-stopped resources).
- Ensure state required by on_stop is not destroyed before stop is invoked.
- Verify the Python class defines a zero-argument on_stop.
Example fix
// before
def on_stop(self):
self.conn.close() # raises if already closed
// after
def on_stop(self):
if self.conn is not None and not self.conn.closed:
self.conn.close() Defensive patterns
Strategy: try-catch
Validate before calling
# Python: make stop idempotent before shutdown if getattr(self, "stopped", False): return
Try / catch
if let Err(e) = actor.on_stop() {
log::error!("python on_stop failed: {e:#}"); // still proceed with teardown
} Prevention
- Write on_stop defensively: wrap each cleanup step so one failure doesn't abort the rest.
- Track a stopped/disposed flag to make shutdown hooks idempotent.
- Keep shutdown-time state (handlers, connections) alive until on_stop completes.
When it happens
Trigger: Calling on_stop when the Python on_stop callback raises — e.g. errors while flushing/closing resources, or a bad no-arg dispatch.
Common situations: Closing an already-closed connection in on_stop; exception during final state persistence; callbacks that reference handlers cleared during shutdown.
Related errors
- Python on_start failed: {e}
- Python on_resume failed: {e}
- Python on_reset failed: {e}
- Python on_dispose failed: {e}
- Python on_degrade failed: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/7b73a4f3228aa9fd.
Report an issue: GitHub.