nautechsystems/nautilus_trader · error

Invalid `OrderSide`

Error message

Invalid `OrderSide`

What it means

`AccountPosition::from_row` in positions.rs parses the `entry` column as an `OrderSide` via `OrderSide::from_str` and unwraps it with `.expect("Invalid `OrderSide`")`. If the stored string is not a valid `OrderSide` ('BUY'/'SELL'), the parse returns `None` and row deserialization panics. The library assumes positions rows were written only through its own serialization.

Source

Thrown at crates/infrastructure/src/sql/models/positions.rs:53

        let trader_id = row.try_get::<&str, _>("trader_id").map(TraderId::from)?;
        let strategy_id = row
            .try_get::<&str, _>("strategy_id")
            .map(StrategyId::from)?;
        let instrument_id = row
            .try_get::<&str, _>("instrument_id")
            .map(InstrumentId::from)?;
        let account_id = row.try_get::<&str, _>("account_id").map(AccountId::from)?;
        let opening_order_id = row
            .try_get::<&str, _>("opening_order_id")
            .map(ClientOrderId::from)?;
        let closing_order_id = row
            .try_get::<Option<&str>, _>("closing_order_id")
            .ok()
            .and_then(|x| x.map(ClientOrderId::from));
        let entry = row
            .try_get::<&str, _>("entry")
            .map(OrderSide::from_str)?
            .expect("Invalid `OrderSide`");
        let side = row
            .try_get::<&str, _>("side")
            .map(PositionSide::from_str)?
            .expect("Invalid `PositionSide`");
        let signed_qty = row.try_get::<f64, _>("signed_qty")?;
        let quantity = row.try_get::<&str, _>("quantity").map(Quantity::from)?;
        let peak_qty = row.try_get::<&str, _>("peak_qty").map(Quantity::from)?;
        let quote_currency = row
            .try_get::<&str, _>("quote_currency")
            .map(Currency::from)?;
        let base_currency = row
            .try_get::<Option<&str>, _>("base_currency")
            .ok()
            .and_then(|x| x.map(Currency::from));
        let settlement_currency = row
            .try_get::<&str, _>("settlement_currency")
            .map(Currency::from)?;
        let avg_px_open = row.try_get::<f64, _>("avg_px_open")?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the failing row's `entry` value in Postgres and rewrite it as a valid `OrderSide` ('BUY' or 'SELL')
  2. Audit all writers of the positions table to ensure they serialize `OrderSide` via the library's `to_string`
  3. Run `SELECT DISTINCT entry FROM positions` and validate every distinct value against `OrderSide::from_str`
  4. Rebuild corrupt rows from the underlying order/fill events rather than ad-hoc string edits

Example fix

// before
entry = 'buy'
// after
entry = 'BUY'
Defensive patterns

Strategy: validation

Validate before calling

fn valid_order_side(s: &str) -> bool { OrderSide::from_str(s).is_some() }
// check before persisting or accepting positions rows

Type guard

fn parse_order_side(s: &str) -> Option<OrderSide> { OrderSide::from_str(s) }

Prevention

When it happens

Trigger: Reading a `positions` row whose `entry` column is not exactly a valid `OrderSide` string (e.g. empty, 'buy' in unexpected casing, 'LONG', or numeric exchange side codes like '1'/'B'), typically from manual inserts or a version/schema mismatch.

Common situations: Manual DB edits or ETL scripts writing position rows; restoring rows from a different nautilus schema version; adapters persisting raw venue side strings instead of the nautilus enum text; typos in seed data.

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/d33a476a85af7968. Report an issue: GitHub.