nautechsystems/nautilus_trader · critical
Invalid `TimeInForce`
Error message
Invalid `TimeInForce`
What it means
The `time_in_force` column string is parsed with `TimeInForce::from_str` and unwrapped with expect() while loading an order row. The panic indicates the stored TIF value (expected e.g. 'GTC','IOC','FOK','GTD','DAY','AT_THE_OPEN'...) is not a recognized variant, so the row cannot be reconstructed.
Source
Thrown at crates/infrastructure/src/sql/models/orders.rs:1094
.try_get::<Option<&str>, _>("trigger_type")
.ok()
.and_then(parse_trigger_type);
let limit_offset = row
.try_get::<Option<&str>, _>("limit_offset")
.ok()
.and_then(|x| x.and_then(|s| Decimal::from_str(s).ok()));
let trailing_offset = row
.try_get::<Option<&str>, _>("trailing_offset")
.ok()
.and_then(|x| x.and_then(|s| Decimal::from_str(s).ok()));
let trailing_offset_type = row
.try_get::<Option<TrailingOffsetTypePg>, _>("trailing_offset_type")
.ok()
.flatten()
.and_then(|value| value.0);
let time_in_force = row
.try_get::<&str, _>("time_in_force")
.map(|x| TimeInForce::from_str(x).expect("Invalid `TimeInForce`"))?;
let expire_time = row
.try_get::<Option<&str>, _>("expire_time")
.ok()
.and_then(|x| x.map(UnixNanos::from));
let filled_qty = row.try_get::<&str, _>("filled_qty").map(Quantity::from)?;
let liquidity_side = row
.try_get::<Option<&str>, _>("liquidity_side")
.ok()
.and_then(|x| x.map(|x| LiquiditySide::from_str(x).expect("Invalid `LiquiditySide`")));
let avg_px = row.try_get::<Option<Decimal>, _>("avg_px").ok().flatten();
let slippage = row.try_get::<Option<Decimal>, _>("slippage").ok().flatten();
let commissions = row
.try_get::<Option<Vec<String>>, _>("commissions")?
.map_or_else(Vec::new, |c| {
c.into_iter().map(|s| Money::from(&s)).collect()
});
let status = row
.try_get::<&str, _>("status")View on GitHub (pinned to 18893faf8b)
Solutions
- Locate bad rows with `SELECT id, time_in_force FROM orders WHERE time_in_force NOT IN (...)` and correct them
- Fix the insertion path to serialize TimeInForce canonically
- Trim/uppercase the stored values via migration if formatting is the issue
- Return a parse error rather than expect for untrusted data sources
Example fix
// before
TimeInForce::from_str(x).expect("Invalid `TimeInForce`")
// after
TimeInForce::from_str(x.trim()).map_err(|e| ModelError::Parse(format!("invalid time_in_force '{x}': {e}")))? Defensive patterns
Strategy: type-guard
Validate before calling
const VALID_TIF: &[&str] = &["GTC","IOC","FOK","GTD","DAY","AT_THE_OPEN","AT_THE_CLOSE"];
assert!(VALID_TIF.contains(&tif_str.trim()), "bad time_in_force: {tif_str}"); Type guard
fn is_known_time_in_force(s: &str) -> bool {
TimeInForce::from_str(s.trim()).is_ok()
} Try / catch
let tif = TimeInForce::from_str(x.trim())
.map_err(|e| ModelError::Parse(format!("row {}: invalid time_in_force '{x}': {e}", row_id)))?; Prevention
- Persist TIF via the enum's canonical string conversion
- Add a CHECK constraint on time_in_force
- Trim and normalize before insert
- Cover all TimeInForce variants in persistence round-trip tests
When it happens
Trigger: Calling `OrderModel::from_row` where `time_in_force` contains an unknown/misspelled/case-mismatched value, e.g. 'Good Till Cancel', 'gtc ' with whitespace, or an exchange-specific TIF not in the enum.
Common situations: Older schema versions or migrations that rewrote TIF values; adapter-specific persistence layers; manual CSV imports into the orders table.
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 `OrderType`
- Invalid `OrderSide`
- Invalid `LiquiditySide`
- Invalid `OrderStatus`
- Invalid `TriggerType`
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/679b55a62e131c72.
Report an issue: GitHub.