nautechsystems/nautilus_trader · critical

Implement FromRow for OptionSpread

Error message

Implement FromRow for OptionSpread

What it means

Same placeholder pattern: `FromRow for OptionSpreadRow` is a `todo!()`, so decoding an option-spread instrument row from Postgres panics with this message. The surrounding CryptoFuturesSpreadRow impl is already written, making it the template to copy. Callers cannot catch this; it aborts the thread.

Source

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

            .maybe_max_notional(max_notional)
            .maybe_min_notional(min_notional)
            .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 OptionSpreadRow {
    fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
        todo!("Implement FromRow for OptionSpread")
    }
}

impl<'r> FromRow<'r, PgRow> for CryptoFuturesSpreadRow {
    #[expect(
        clippy::too_many_lines,
        reason = "SQL row mapping mirrors the full crypto futures spread constructor"
    )]
    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::from)?;
        let underlying = row.try_get::<String, _>("underlying").map(Currency::from)?;
        let quote_currency = row
            .try_get::<String, _>("quote_currency")
            .map(Currency::from)?;
        let settlement_currency = row
            .try_get::<String, _>("settlement_currency")
            .map(Currency::from)?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Implement from_row for OptionSpreadRow following the adjacent OptionContractRow / CryptoFuturesSpreadRow implementations
  2. Exclude option spread rows from queries until implemented
  3. Track upstream as a known unimplemented persistence path

Example fix

// before
fn from_row(_row: &'r PgRow) -> Result<Self, sqlx::Error> {
    todo!("Implement FromRow for OptionSpread")
}
// after
fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
    let id = row.try_get::<String, _>("id").map(InstrumentId::from)?;
    // ... try_get all columns and construct the OptionSpread instrument
    Ok(Self(inst))
}
Defensive patterns

Strategy: validation

Validate before calling

// Exclude option spreads from bulk loads
if instrument.kind == OptionSpread { continue; }

Try / catch

// Avoid decoding into OptionSpreadRow until implemented; filter at SQL level
WHERE instrument_class != 'option_spread'

Prevention

When it happens

Trigger: Any sqlx decode of query results into OptionSpreadRow, e.g. loading option spread instruments from the database.

Common situations: Running an instrument sync/load that includes option spreads; writing a new query against the instruments schema with spread rows present.

Related errors


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