{"record":{"id":"72a828ba0bba3319","repo":"nautechsystems/nautilus_trader","slug":"order-side-must-be-buy-or-sell-72a828","errorCode":null,"errorMessage":"Order side must be Buy or Sell","messagePattern":"Order side must be Buy or Sell","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/ffi/orderbook/level.rs","lineNumber":50,"sourceCode":"///\n/// Panics if `order_side` is `NoOrderSide`.\n///\n/// Returns an owning pointer to the heap-allocated `BookLevel` which the caller must\n/// eventually pass to [`level_drop`].\npub unsafe extern \"C\" fn level_new(\n    order_side: OrderSideOptional,\n    price: Price,\n    orders: CVec,\n) -> *mut BookLevel {\n    let orders = unsafe { orders.into_vec::<BookOrderFfi>() }\n        .into_iter()\n        .map(Into::into)\n        .collect::<Vec<BookOrder>>();\n    let price = BookPrice {\n        value: price,\n        side: order_side\n            .as_option()\n            .expect(\"Order side must be Buy or Sell\"),\n    };\n    let mut level = BookLevel::new(price);\n    level.add_bulk(&orders);\n    Box::into_raw(Box::new(level))\n}\n\n/// # Safety\n///\n/// `level` must be a live owning pointer returned by [`level_new`] or [`level_clone`],\n/// and must not be used after this call.\n///\n/// # Panics\n///\n/// Panics if `level` is null.\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn level_drop(level: *mut BookLevel) {\n    abort_on_panic(|| {\n        assert!(!level.is_null(), \"`level` was NULL\");","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/ffi/orderbook/level.rs#L32-L68","documentation":"The FFI constructor `BookPrice` (in crates/model/src/ffi/orderbook/level.rs) panics when the `order_side` argument cannot be decoded into a Buy/Sell side via `as_option()`. A BookLevel must be tagged with a valid side to know whether to aggregate bid or ask orders, so an invalid side aborts construction. This guards the C ABI against invalid enum input.","triggerScenarios":"Calling the exported `BookPrice` FFI function with a null/invalid order_side handle so `OrderSide::as_option()` returns None while building the BookPrice struct.","commonSituations":"Bindings passing Python None for side, integer side values outside 1..2, or uninitialized FFI struct memory when constructing a book level from raw orders.","solutions":["Pass a concrete OrderSide (BUY or SELL) when constructing the level.","Validate the side value in the caller language before crossing the FFI boundary.","Inspect the call site for a variable that was never assigned a side (defaulted to None).","Rebuild generated bindings if the enum discriminants changed between versions."],"exampleFix":"// before\nlevel = BookPrice(price, None, orders)\n// after\nside = OrderSide.BUY  # or OrderSide.SELL\nlevel = BookPrice(price, side, orders)","handlingStrategy":"validation","validationCode":"# Python caller\nassert side in (OrderSide.BUY, OrderSide.SELL), f\"invalid side: {side}\"\nprice_ptr = BookPrice(price, side, orders)","typeGuard":"def is_valid_side(side) -> bool:\n    return side in (OrderSide.BUY, OrderSide.SELL)","tryCatchPattern":null,"preventionTips":["Default side explicitly (BUY/SELL) rather than leaving it None.","Validate enum values at the binding layer before FFI calls.","Avoid zero-initialized FFI structs where 0 is not a valid discriminant."],"tags":["rust","ffi","panic","invalid-enum"],"backgroundTag":"invalid-enum-value","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"}