nautechsystems/nautilus_trader · error
Limit order must have a price
Error message
Limit order must have a price
What it means
The matching engine's order-filling path matched a LIMIT order whose price is None. A limit order is defined by its price; without one the engine cannot determine the book price level to fill against, so it panics. This invariant failure means a malformed limit order reached the fill logic instead of being rejected earlier.
Source
Thrown at crates/execution/src/matching_engine/mod.rs:4280
self.target_ask = self.core.ask;
self.target_last = self.core.last;
self.core.set_bid_raw(target_price);
self.core.set_last_raw(target_price);
fill.0 = target_price;
}
}
}
}
}
self.apply_liquidity_consumption(
fills,
order.order_side(),
order.leaves_qty(),
book_prices_ref,
)
}
None => panic!("Limit order must have a price"),
}
}
fn determine_market_price_and_volume(&self, order: &OrderAny) -> Vec<(Price, Quantity)> {
let price = match order.order_side() {
OrderSide::Buy => Price::max(FIXED_PRECISION),
OrderSide::Sell => Price::min(FIXED_PRECISION),
};
// When liquidity consumption is enabled, get ALL crossed levels so that
// consumed levels can be filtered out while still finding valid ones.
let mut fills = if self.config.liquidity_consumption {
let size_prec = self.instrument.size_precision();
self.book
.get_all_crossed_levels(order.order_side(), price, size_prec)
} else {
let book_order = BookOrder::new(order.order_side(), price, order.quantity(), 0);
self.book.simulate_fills(&book_order)View on GitHub (pinned to 18893faf8b)
Solutions
- Always set a valid Price when creating a LIMIT order (use instrument.make_price(...) to get correct precision).
- Add a pre-submission check that limit orders carry a price so they are rejected at the strategy/client layer instead of panicking in the engine.
- Check serialization/deserialization of orders (e.g. redis-backed caches) so the price field is not dropped.
- Verify order_type classification in adapter conversions between venue and Nautilus order types.
Example fix
// before OrderFactory::limit(instrument_id, OrderSide::Buy, qty) // price omitted // after let price = instrument.make_price(100.25); OrderFactory::limit(instrument_id, OrderSide::Buy, qty, Price(price))
Defensive patterns
Strategy: validation
Validate before calling
if order.order_type() == OrderType::Limit && order.price().is_none() {
return Err(anyhow!("limit order {} missing price", order.client_order_id()));
} Type guard
fn limit_has_price(order: &OrderAny) -> bool {
order.order_type() != OrderType::Limit || order.price().is_some()
} Prevention
- Use instrument.make_price(...) when constructing limit orders to guarantee valid precision and value.
- Validate order invariants at the strategy layer before submission.
- Check that cache/serialization round-trips preserve the price field.
- In adapters, never emit Limit orders derived from price-less venue messages.
When it happens
Trigger: Calling the limit fill routine (determine_price_and_volume-style path around matching_engine/mod.rs:4280) with an OrderAny of type Limit whose price() returns None (match arm `None => panic!(...)`).
Common situations: Constructing a Limit order without setting price (or with price lost during deserialization/conversion); adapter code misclassifying a market order as limit; corrupt order state after cache round-trip.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Matching engine not found for instrument {order_instrument_i
- Matching engine not found for instrument {instrument_id}
- Position id should be generated. Hedging Oms type order matc
- OTO parent not found
- Cannot find contingent order for {client_order_id}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/bc25546886bae1e8.
Report an issue: GitHub.