{"record":{"id":"41912624f101bc89","repo":"nautechsystems/nautilus_trader","slug":"bookorder-side-must-be-buy-or-sell","errorCode":null,"errorMessage":"BookOrder side must be Buy or Sell","messagePattern":"BookOrder side must be Buy or Sell","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/data/order.rs","lineNumber":96,"sourceCode":"    ) -> Self {\n        Self {\n            side: side.into(),\n            price,\n            size,\n            order_id,\n        }\n    }\n\n    /// Returns a [`BookPrice`] from this order.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `self.side` is `None`.\n    #[must_use]\n    pub fn to_book_price(&self) -> BookPrice {\n        BookPrice::new(\n            self.price,\n            self.side.expect(\"BookOrder side must be Buy or Sell\"),\n        )\n    }\n\n    /// Returns the order exposure as an `f64`.\n    #[must_use]\n    pub fn exposure(&self) -> f64 {\n        self.price.as_f64() * self.size.as_f64()\n    }\n\n    /// Returns the signed order size as `f64`, positive for buys, negative for sells.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `self.side` is `None`.\n    #[must_use]\n    pub fn signed_size(&self) -> f64 {\n        match self.side {\n            Some(OrderSide::Buy) => self.size.as_f64(),","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/data/order.rs#L78-L114","documentation":"BookOrder::to_book_price() converts the order's price into a BookPrice, but BookOrder.side is an Option<OrderSide>. The library panics via expect when side is None because a book order without a side has no valid price interpretation. It is a deliberate fail-fast for a struct that was constructed incompletely.","triggerScenarios":"Calling to_book_price() on a BookOrder whose side field is None — typically a BookOrder built with side: None (e.g. from a partial L1/L2 update that did not carry a side, or a default-constructed order) then passed into book assembly paths like add, replace_l1, or book integrity checks.","commonSituations":"Feeding market-data deltas where an L1 update lacks a side; constructing BookOrder manually for tests with side omitted; deserializing a partial order book snapshot that dropped the side field.","solutions":["Ensure the BookOrder is constructed with a valid Some(OrderSide::Buy) or Some(OrderSide::Sell) before calling to_book_price().","Check side.is_some() before calling to_book_price(), or match on the Option and handle the None case explicitly.","If the order came from an upstream feed, fix the parser/adapter so the side is always populated from the message."],"exampleFix":"// before\nlet book_price = order.to_book_price();\n// after\nlet book_price = order\n    .side\n    .map(|_| order.to_book_price())\n    .unwrap_or_else(|| panic!(\"BookOrder {:?} has no side; skipping\", order.order_id));","handlingStrategy":"type-guard","validationCode":"if order.side.is_none() { /* skip or reconstruct from feed */ }","typeGuard":"fn has_side(order: &BookOrder) -> bool { order.side.is_some() }","tryCatchPattern":"// Rust panics are not catchable idiomatic; guard instead:\nlet price = order.side.map(|_| order.to_book_price());","preventionTips":["Always construct BookOrder with a known OrderSide from the feed message.","Validate L1/L2 updates carry a side before book assembly.","Add a debug_assert in feed adapters that side is populated."],"tags":["rust","panic","option-unwrap","order-book"],"backgroundTag":"null-argument","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}