{"record":{"id":"e2faad48917ae699","repo":"nautechsystems/nautilus_trader","slug":"noorderside","errorCode":null,"errorMessage":"NoOrderSide","messagePattern":"NoOrderSide","errorType":"panic","errorClass":"BookIntegrityError","httpStatus":null,"severity":"critical","filePath":"crates/model/src/data/order.rs","lineNumber":116,"sourceCode":"    }\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(),\n            Some(OrderSide::Sell) => -(self.size.as_f64()),\n            None => panic!(\"{}\", BookIntegrityError::NoOrderSide),\n        }\n    }\n}\n\nimpl Default for BookOrder {\n    /// Creates a NULL [`BookOrder`] instance.\n    fn default() -> Self {\n        NULL_ORDER\n    }\n}\n\nimpl PartialEq for BookOrder {\n    fn eq(&self, other: &Self) -> bool {\n        self.order_id == other.order_id\n    }\n}\n\nimpl Hash for BookOrder {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/data/order.rs#L98-L134","documentation":"This panic comes from `BookOrder::signed_size()`, which returns size as a sign-carrying f64: positive for `OrderSide::Buy`, negative for `OrderSide::Sell`. If `self.side` is `None` the order has no directional side (e.g. a NULL/clear book order), the sign is undefined, and the method panics with `BookIntegrityError::NoOrderSide` rather than returning a misleading zero.","triggerScenarios":"Calling `order.signed_size()` on a `BookOrder` whose `side` field is `None` — typically a NULL-order sentinel, a book-clear/delete action decoded with side 'N', or an order struct constructed without setting `side`. Exercised by `test_signed_size` (valid sides) and exposed to Python via `py_signed_size`.","commonSituations":"Iterating all orders in a `Book` (which contains NULL placeholder orders) and computing signed aggregate size without filtering; handling Databento MBO Clear actions whose side is 'N'; deserializing order snapshots where side was omitted.","solutions":["Check `order.side.is_some()` (or match on it) before calling `signed_size()` and skip `None`-sided orders.","Use `match order.side` to handle Buy/Sell/None explicitly instead of relying on the panicking helper.","Filter out NULL/clear placeholder orders when aggregating book size so only real orders contribute."],"exampleFix":"// before\nlet signed = order.signed_size();\n// after\nlet signed = match order.side {\n    Some(OrderSide::Buy) => order.size.as_f64(),\n    Some(OrderSide::Sell) => -order.size.as_f64(),\n    None => 0.0, // skip NULL / clear orders\n};","handlingStrategy":"type-guard","validationCode":"if order.side.is_none() {\n    // skip NULL / clear placeholder orders\n    return 0.0;\n}","typeGuard":"fn real_order(order: &BookOrder) -> bool {\n    matches!(order.side, Some(OrderSide::Buy) | Some(OrderSide::Sell))\n}","tryCatchPattern":"// signed_size panics; avoid catching by matching side explicitly\nlet signed = match order.side {\n    Some(side) => signed_size_for(side, order.size.as_f64()),\n    None => 0.0,\n};","preventionTips":["Filter NULL/clear orders (side 'N' in MBO feeds) before signed-size aggregation","Always initialize BookOrder.side when constructing orders manually","Treat side-less orders as book-maintenance events, not size-bearing orders"],"tags":["rust","panic","order-book","missing-side"],"backgroundTag":"null-argument","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}