nautechsystems/nautilus_trader · error

{e}

Error message

{e}

What it means

When a Python FillModel's `fill_limit_inside_spread` method is called from Rust, the Python call or extraction of its boolean result failed. The raw Python error (`{e}`) is surfaced so the underlying Python-side failure is visible.

Source

Thrown at crates/execution/src/python/fill.rs:105

impl FillModel for PythonFillModel {
    fn is_limit_filled(&mut self) -> anyhow::Result<bool> {
        call_bool_method(&self.obj, "is_limit_filled")
    }

    fn is_slipped(&mut self) -> anyhow::Result<bool> {
        call_bool_method(&self.obj, "is_slipped")
    }

    fn fill_limit_inside_spread(&self) -> anyhow::Result<bool> {
        Python::attach(|py| -> anyhow::Result<bool> {
            let obj = self.obj.bind(py);
            if !obj.hasattr("fill_limit_inside_spread")? {
                return Ok(false);
            }

            obj.call_method0("fill_limit_inside_spread")?
                .extract()
                .map_err(|e| anyhow::anyhow!("{e}"))
        })
        .map_err(|e| anyhow::anyhow!("Python FillModel.fill_limit_inside_spread failed: {e}"))
    }

    fn get_orderbook_for_fill_simulation(
        &mut self,
        instrument: &InstrumentAny,
        order: &OrderAny,
        best_bid: Price,
        best_ask: Price,
    ) -> anyhow::Result<Option<OrderBook>> {
        Python::attach(|py| -> anyhow::Result<Option<OrderBook>> {
            let obj = self.obj.bind(py);
            if !obj.hasattr("get_orderbook_for_fill_simulation")? {
                return Ok(None);
            }

            let instrument = instrument_any_to_pyobject(py, instrument.clone())?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read `{e}` for the underlying Python exception and fix the Python fill_limit_inside_spread implementation
  2. Ensure the method takes no arguments and returns a plain Python bool
  3. Confirm the object actually implements fill_limit_inside_spread (Rust skips the call if the attribute is absent, so it exists but misbehaved)
  4. Add unit tests covering inside-spread limit order scenarios

Example fix

// before
def fill_limit_inside_spread(self):
    return self.prob  # numpy float, extraction fails
// after
def fill_limit_inside_spread(self):
    return random.random() < self.prob  # plain bool
Defensive patterns

Strategy: type-guard

Validate before calling

assert callable(getattr(fill_model, "fill_limit_inside_spread", None)), "FillModel missing fill_limit_inside_spread"

Type guard

def is_valid_fill_model(m) -> bool:
    fn = getattr(m, "fill_limit_inside_spread", None)
    return callable(fn)

Try / catch

let inside = match fill_model.fill_limit_inside_spread() {
    Ok(b) => b,
    Err(e) => { log::error!("fill model failed: {e:#}"); false }
};

Prevention

When it happens

Trigger: Calling FillModel.fill_limit_inside_spread when the Python method raises an exception or does not return a value extractable to bool.

Common situations: A custom fill model returns None or a numpy bool instead of a plain bool; the Python method raises AttributeError on missing attributes; signature drift after a version upgrade.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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