nautechsystems/nautilus_trader · critical

Implement FromRow for FuturesSpread

Error message

Implement FromRow for FuturesSpread

What it means

This is a deliberate `todo!()` placeholder in `FromRow for FuturesSpreadRow`, meaning futures-spread instrument rows cannot yet be deserialized from Postgres. Any query returning FuturesSpreadRow via sqlx panics with this message instead of returning a row. It is unfinished implementation, not a runtime condition the caller caused.

Source

Thrown at crates/infrastructure/src/sql/models/instruments.rs:998

            .maybe_max_quantity(max_quantity)
            .maybe_min_quantity(min_quantity)
            .maybe_max_price(max_price)
            .maybe_min_price(min_price)
            .maybe_margin_init(margin_init)
            .maybe_margin_maint(margin_maint)
            .maybe_maker_fee(maker_fee)
            .maybe_taker_fee(taker_fee)
            .ts_event(ts_event)
            .ts_init(ts_init)
            .build()
            .unwrap();
        Ok(Self(inst))
    }
}

impl<'r> FromRow<'r, PgRow> for FuturesSpreadRow {
    fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
        todo!("Implement FromRow for FuturesSpread")
    }
}

impl<'r> FromRow<'r, PgRow> for OptionContractRow {
    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
        let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
        let raw_symbol = row.try_get::<String, _>("raw_symbol").map(Symbol::new)?;
        let asset_class = row
            .try_get::<AssetClassPg, _>("asset_class")
            .map(|res| res.0)?;
        let exchange = row
            .try_get::<Option<String>, _>("exchange")
            .map(|res| res.map(|s| Ustr::from(s.as_str())))?;
        let underlying = row
            .try_get::<String, _>("underlying")
            .map(|res| Ustr::from(res.as_str()))?;
        let option_kind = row
            .try_get::<String, _>("option_kind")

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Implement from_row for FuturesSpreadRow by try_get-ing each column and constructing the inner instrument (mirror the adjacent OptionContractRow impl)
  2. As a stopgap, filter queries to exclude futures spread rows until the impl lands
  3. Raise/track an upstream issue in nautilus_infrastructure that FuturesSpread persistence is unimplemented

Example fix

// before
fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
    todo!("Implement FromRow for FuturesSpread")
}
// after
fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
    let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
    let raw_symbol = row.try_get::<String, _>("raw_symbol")?.into();
    // ... try_get remaining columns, build InstrumentAny::FuturesSpread(...)
    Ok(Self(inst))
}
Defensive patterns

Strategy: validation

Validate before calling

// Guard before decoding
if matches!(instrument_def, InstrumentDef::FuturesSpread(_)) {
    return Err("FuturesSpread persistence not implemented".into());
}

Try / catch

// todo! panics cannot be caught safely; filter rows instead
let rows: Vec<ContractRow> = sqlx::query(query).fetch_all(&pool).await?;

Prevention

When it happens

Trigger: Any sqlx query whose output is decoded into FuturesSpreadRow via PgRow, e.g. fetching futures spread instruments from the instruments table.

Common situations: Developer writes or runs a query loading futures spread instruments; a load-all instruments routine reaches the spread branch; integration test exercising instrument persistence for spreads.

Related errors


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