nautechsystems/nautilus_trader · error

Python on_order failed: {e}

Error message

Python on_order failed: {e}

What it means

PyExecutionAlgorithm wraps a Python execution algorithm; when the Rust `ExecutionAlgorithm::on_order` trait method is invoked it dispatches into Python (`dispatch_on_order`). Any Python-side exception (or PyO3 boundary error) is re-wrapped as a Rust anyhow error with this message. It marks that the Python `on_order` callback itself failed, not the surrounding Rust pipeline.

Source

Thrown at crates/trading/src/python/algorithm.rs:393

    fn core_mut(&mut self) -> &mut DataActorCore {
        DataActorNative::core_mut(&mut self.inner_mut().core)
    }
}

impl ExecutionAlgorithmNative for PyExecutionAlgorithm {
    fn exec_algorithm_core(&self) -> &ExecutionAlgorithmCore {
        &self.inner().core
    }

    fn exec_algorithm_core_mut(&mut self) -> &mut ExecutionAlgorithmCore {
        &mut self.inner_mut().core
    }
}

impl ExecutionAlgorithm for PyExecutionAlgorithm {
    fn on_order(&mut self, order: OrderAny) -> anyhow::Result<()> {
        self.dispatch_on_order(order)
            .map_err(|e| anyhow::anyhow!("Python on_order failed: {e}"))
    }

    fn on_order_list(
        &mut self,
        order_list: OrderList,
        orders: Vec<OrderAny>,
    ) -> anyhow::Result<()> {
        if self
            .has_python_override("on_order_list")
            .map_err(|e| anyhow::anyhow!("Python override lookup failed: {e}"))?
        {
            return self
                .dispatch_on_order_list(order_list, orders)
                .map_err(|e| anyhow::anyhow!("Python on_order_list failed: {e}"));
        }

        for order in orders {
            self.on_order(order)?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the chained `{e}` message; it contains the original Python traceback/exception and fix the Python on_order implementation.
  2. Reproduce the failing order in Python directly and run the on_order callback to get the full traceback.
  3. Verify the Python class actually defines on_order with the expected signature for the current pyo3-asyncio/bindings version.
  4. Confirm Python objects/imports are valid at construction time so the dispatch target exists.

Example fix

// Python side
class MyAlgo(ExecutionAlgorithm):
    def on_order(self, order):        # before: def on_order(self, order, ctx):  -> TypeError
        ...
Defensive patterns

Strategy: try-catch

Validate before calling

# Python: smoke-test the callback before registering the algorithm
assert callable(getattr(algo, "on_order", None)), "on_order override missing"

Type guard

fn overrides_on_order(py_algo: &PyAny) -> pyo3::PyResult<bool> {
    py_algo.call_method0("__class__")?.getattr("on_order")
        .map(|_| true)
}

Try / catch

match algo.on_order(order) {
    Ok(()) => {},
    Err(e) => log::error!("algorithm on_order failed: {e:#}"), // {e} carries the Python traceback
}

Prevention

When it happens

Trigger: Calling on_order (directly or via on_order_list fallback loop) when the Python handler raises an exception, has a bad signature, or the interpreter/object reference is invalid.

Common situations: A bug in a user-written Python ExecutionAlgorithm.on_order override (TypeError, AttributeError, unhandled domain error); calling a removed/renamed Python method; Python callback expecting different argument types than the OrderAny conversion provides.

Related errors


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