nautechsystems/nautilus_trader · error
Python on_time_event failed: {e}
Error message
Python on_time_event failed: {e} What it means
Raised by the Rust `ExecutionAlgorithm` wrapper when the Python callback `on_time_event` throws an exception. The wrapper first runs the native Rust `ExecutionAlgorithm::on_time_event`, then dispatches the `TimeEvent` into the Python subclass via `dispatch_time_event`; a `PyErr` from the user's handler is wrapped in `anyhow!("Python on_time_event failed: {e}")`.
Source
Thrown at crates/trading/src/python/algorithm.rs:586
fn on_dispose(&mut self) -> anyhow::Result<()> {
self.dispatch_no_args("on_dispose")
.map_err(|e| anyhow::anyhow!("Python on_dispose failed: {e}"))
}
fn on_degrade(&mut self) -> anyhow::Result<()> {
self.dispatch_no_args("on_degrade")
.map_err(|e| anyhow::anyhow!("Python on_degrade failed: {e}"))
}
fn on_fault(&mut self) -> anyhow::Result<()> {
self.dispatch_no_args("on_fault")
.map_err(|e| anyhow::anyhow!("Python on_fault failed: {e}"))
}
fn on_time_event(&mut self, event: &TimeEvent) -> anyhow::Result<()> {
ExecutionAlgorithm::on_time_event(self, event)?;
self.dispatch_time_event(event)
.map_err(|e| anyhow::anyhow!("Python on_time_event failed: {e}"))
}
fn on_signal(&mut self, signal: &Signal) -> anyhow::Result<()> {
self.dispatch_on_signal(signal)
.map_err(|e| anyhow::anyhow!("Python on_signal failed: {e}"))
}
fn on_queue_state(&mut self, event: &QueueStateChanged) -> anyhow::Result<()> {
self.dispatch_on_queue_state(event)
.map_err(|e| anyhow::anyhow!("Python on_queue_state failed: {e}"))
}
fn on_socket_state(&mut self, event: &SocketStateChanged) -> anyhow::Result<()> {
self.dispatch_on_socket_state(event)
.map_err(|e| anyhow::anyhow!("Python on_socket_state failed: {e}"))
}
}
View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the embedded Python traceback in the error to locate the failing line in your `on_time_event` override
- Handle the incoming `TimeEvent` generically (check `event.name`/`event.tags`) instead of assuming a specific timer
- Add try/except inside `on_time_event` for expected data conditions and log the failure
- Confirm the override signature is `def on_time_event(self, event) -> None:`
- Re-register or cancel stale timers in `on_stop`/`on_fault` so no handler fires against torn-down state
Example fix
# before
def on_time_event(self, event):
price = self.cache.price(self.instrument_id) # raises if no price yet
# after
def on_time_event(self, event):
price = self.cache.price(self.instrument_id)
if price is None:
self.log.warning(f"No price yet for {event.name}")
return Defensive patterns
Strategy: try-catch
Validate before calling
sig = inspect.signature(MyAlgo.on_time_event) assert list(sig.parameters) == ['self', 'event']
Type guard
def is_time_event(ev):
return hasattr(ev, 'ts_event') and hasattr(ev, 'name') # TimeEvent surface Try / catch
try:
algo.on_time_event(event)
except Exception as e:
log.error(f"on_time_event failed for timer '{event.name}': {e}") Prevention
- Handle events by timer name/tag defensively; never assume a specific event type
- Cancel timers in `on_stop` so handlers cannot fire after teardown
- Avoid cache/network lookups in timer handlers without null checks
- Unit-test timer handlers with representative `TimeEvent` fixtures
When it happens
Trigger: A registered timer fires and the trader calls `on_time_event(event)`; the user's Python subclass overrides `on_time_event` and raises (e.g. mishandles the event type, wrong event class assumed, or an exception inside timed logic). Also triggered by `algo.on_time_event(event)` called directly with a `TimeEvent`.
Common situations: Timers set with `clock.set_timer` whose handlers unpack event fields that don't exist after a version change; logic assuming a specific `TimeEvent` name/tag; unhandled exceptions inside backtest loops at timer expiry.
Related errors
- Python on_fault failed: {e}
- Python on_signal failed: {e}
- Python on_queue_state failed: {e}
- Python on_socket_state failed: {e}
- Python on_pool_swap failed: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ecff8b0ab3ba1c31.
Report an issue: GitHub.