nautechsystems/nautilus_trader · error

Python on_queue_state failed: {e}

Error message

Python on_queue_state failed: {e}

What it means

Raised by the Rust `ExecutionAlgorithm` wrapper when the Python callback `on_queue_state` throws. Internal `QueueStateChanged` events (message-queue state transitions) are dispatched to the Python subclass via `dispatch_on_queue_state`; a `PyErr` from the user's handler is wrapped as `anyhow!("Python on_queue_state failed: {e}")`.

Source

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

    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}"))
    }
}

#[pyo3::pymethods]
#[pyo3_stub_gen::derive::gen_stub_pymethods]
#[allow(
    clippy::large_types_passed_by_value,
    reason = "PyO3 callbacks accept Python-owned event values"
)]
#[expect(
    clippy::unused_self,
    reason = "default PyO3 callbacks must remain instance methods"
)]

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the embedded Python traceback to find the failing statement in your `on_queue_state` override
  2. Match the override signature exactly: `def on_queue_state(self, event) -> None:` and only read documented `QueueStateChanged` fields
  3. Guard the handler body with try/except so queue monitoring never disrupts trading
  4. If you don't need the hook, remove the override so the base no-op runs
  5. Check the nautilus_trader changelog for `QueueStateChanged` field changes if this appeared after an upgrade

Example fix

# before
def on_queue_state(self, event):
    if event.state == QueueState.FULL:  # AttributeError: wrong field

# after
def on_queue_state(self, event):
    try:
        if event.is_full:
            self.log.warning("Queue full")
    except Exception as e:
        self.log.error(f"queue-state handler failed: {e}")
Defensive patterns

Strategy: try-catch

Validate before calling

sig = inspect.signature(MyAlgo.on_queue_state)
assert list(sig.parameters) == ['self', 'event']

Type guard

def is_queue_state_event(ev):
    return type(ev).__name__ == 'QueueStateChanged'

Try / catch

try:
    algo.on_queue_state(event)
except Exception as e:
    log.error(f"on_queue_state handler failed: {e}")  # never let monitoring hooks break trading

Prevention

When it happens

Trigger: A `QueueStateChanged` event is emitted (queue becomes full/drainable/idle) and the registered algorithm's Python subclass overrides `on_queue_state` and raises — typically by assuming attributes or event fields that don't exist, or by raising inside backpressure handling logic.

Common situations: Rarely overridden intentionally; hit when a user overrides the hook to monitor queue pressure but references the wrong field name of `QueueStateChanged`, or performs actions (e.g. submitting orders) that themselves fail during backpressure.

Related errors


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