nautechsystems/nautilus_trader · error
Invalid `PositionSide`
Error message
Invalid `PositionSide`
What it means
`AccountPosition::from_row` parses the `side` column as a `PositionSide` via `PositionSide::from_str` and unwraps with `.expect("Invalid `PositionSide`")`. A `side` value that is not a valid `PositionSide` variant ('LONG', 'SHORT', 'FLAT') causes the row deserialization to panic. This is a defensive guard against corrupted or foreign-written rows.
Source
Thrown at crates/infrastructure/src/sql/models/positions.rs:57
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")?;
let avg_px_close = row.try_get::<Option<f64>, _>("avg_px_close")?;
let realized_return = row.try_get::<Option<f64>, _>("realized_return")?;
let realized_pnl = row.try_get::<&str, _>("realized_pnl").map(Money::from)?;
let unrealized_pnl = rowView on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the `side` value of the failing row and rewrite it as a valid `PositionSide` ('LONG', 'SHORT', or 'FLAT')
- Run `SELECT DISTINCT side FROM positions` and validate each value against `PositionSide::from_str`
- Fix the writer/adapter that produced the non-canonical string so it uses the library serializer
- Rebuild affected rows from underlying order/fill events if source data is available
Example fix
// before side = 'long' // after side = 'LONG'
Defensive patterns
Strategy: validation
Validate before calling
fn valid_position_side(s: &str) -> bool { PositionSide::from_str(s).is_some() }
// check before persisting or accepting positions rows Type guard
fn parse_position_side(s: &str) -> Option<PositionSide> { PositionSide::from_str(s) } Prevention
- Persist only canonical 'LONG'/'SHORT'/'FLAT' strings
- Validate imported/foreign data against the enum before insert
- Avoid manual edits on the positions table; rebuild from events
When it happens
Trigger: Reading a `positions` row whose `side` column contains an unrecognized string (e.g. empty, lowercase 'long', 'LONG_POSITION'), typically from manual inserts, ETL imports, or a schema/version mismatch between writer and reader.
Common situations: Manual data fixes on the positions table; importing positions from another system with different side naming; backup restores across nautilus versions with changed enum values.
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
- Invalid `ContingencyType`
- Invalid `OrderSide`
- Execution intent {} has unknown purpose {}
- Invalid scientific notation exponent '{exponent}': must be a
- Invalid NodeState value
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/5194e1b7590018ce.
Report an issue: GitHub.