nautechsystems/nautilus_trader · critical

Invalid `OrderSide`

Error message

Invalid `OrderSide`

What it means

Same row-hydration pattern as order_type: the `order_side` column string is parsed with `OrderSide::from_str` and unwrapped with expect(). The panic means the stored side string (expected values like 'BUY'/'SELL') is unrecognized, so the order row cannot be deserialized.

Source

Thrown at crates/infrastructure/src/sql/models/orders.rs:1061

            .and_then(|x| x.map(VenueOrderId::from));
        let position_id = row
            .try_get::<Option<&str>, _>("position_id")
            .ok()
            .and_then(|x| x.map(PositionId::from));
        let account_id = row
            .try_get::<Option<&str>, _>("account_id")
            .ok()
            .and_then(|x| x.map(AccountId::from));
        let last_trade_id = row
            .try_get::<Option<&str>, _>("last_trade_id")
            .ok()
            .and_then(|x| x.map(TradeId::from));
        let order_type = row
            .try_get::<&str, _>("order_type")
            .map(|x| OrderType::from_str(x).expect("Invalid `OrderType`"))?;
        let order_side = row
            .try_get::<&str, _>("order_side")
            .map(|x| OrderSide::from_str(x).expect("Invalid `OrderSide`"))?;
        let quantity = row.try_get::<&str, _>("quantity").map(Quantity::from)?;
        let price = row
            .try_get::<Option<&str>, _>("price")
            .ok()
            .and_then(|x| x.map(Price::from));
        let activation_price = row
            .try_get::<Option<&str>, _>("activation_price")
            .ok()
            .and_then(|x| x.map(Price::from));
        let trigger_price = row
            .try_get::<Option<&str>, _>("trigger_price")
            .ok()
            .and_then(|x| x.map(Price::from));
        let trigger_type = row
            .try_get::<Option<&str>, _>("trigger_type")
            .ok()
            .and_then(parse_trigger_type);
        let limit_offset = row

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Find and fix bad rows: `SELECT id, order_side FROM orders WHERE order_side NOT IN ('BUY','SELL')`
  2. Normalize the writer to persist canonical 'BUY'/'SELL' strings
  3. If case is the only problem, normalize with UPPER() in a data migration
  4. Propagate a parse error instead of expect when handling untrusted data

Example fix

// before
OrderSide::from_str(x).expect("Invalid `OrderSide`")
// after
OrderSide::from_str(&x.to_uppercase()).map_err(|_| ModelError::Parse(format!("bad order_side '{x}'")))?
Defensive patterns

Strategy: type-guard

Validate before calling

assert!(matches!(order_side_str, "BUY" | "SELL"), "bad order_side: {order_side_str}");

Type guard

fn is_known_order_side(s: &str) -> bool {
    OrderSide::from_str(s).is_ok()
}

Try / catch

let order_side = OrderSide::from_str(x)
    .map_err(|e| ModelError::Parse(format!("row {}: invalid order_side '{x}': {e}", row_id)))?;

Prevention

When it happens

Trigger: Calling `OrderModel::from_row` where `order_side` holds values like 'buy' (wrong case), 'B'/'S', a localized string, or data from a foreign schema.

Common situations: Tables shared with non-nautilus writers; case-sensitivity mismatches after manual data loads; adapters persisting their own side naming.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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