nautechsystems/nautilus_trader · error

Missing root_symbol for {}

Error message

Missing root_symbol for {}

What it means

The TryFrom<BitmexInstrumentMsg> conversion for instrument definitions requires `root_symbol`, but the WebSocket instrument message omitted it (null). The conversion fails so an incomplete instrument definition is never used downstream.

Source

Thrown at crates/adapters/bitmex/src/websocket/messages.rs:582

    pub indicative_tax_rate: Option<f64>,
    pub open_interest: Option<i64>,
    pub open_value: Option<i64>,
    pub fair_basis: Option<f64>,
    pub fair_basis_rate: Option<f64>,
    pub fair_price: Option<f64>,
    pub timestamp: Timestamp,
}

impl TryFrom<BitmexInstrumentMsg> for crate::http::models::BitmexInstrument {
    type Error = anyhow::Error;

    fn try_from(msg: BitmexInstrumentMsg) -> Result<Self, Self::Error> {
        use crate::common::enums::{BitmexInstrumentState, BitmexInstrumentType};

        // Required fields
        let root_symbol = msg
            .root_symbol
            .ok_or_else(|| anyhow::anyhow!("Missing root_symbol for {}", msg.symbol))?;
        let underlying = msg
            .underlying
            .ok_or_else(|| anyhow::anyhow!("Missing underlying for {}", msg.symbol))?;
        let quote_currency = msg
            .quote_currency
            .ok_or_else(|| anyhow::anyhow!("Missing quote_currency for {}", msg.symbol))?;
        let tick_size = msg
            .tick_size
            .ok_or_else(|| anyhow::anyhow!("Missing tick_size for {}", msg.symbol))?;
        let multiplier = msg
            .multiplier
            .ok_or_else(|| anyhow::anyhow!("Missing multiplier for {}", msg.symbol))?;
        let is_quanto = msg
            .is_quanto
            .ok_or_else(|| anyhow::anyhow!("Missing is_quanto for {}", msg.symbol))?;
        let is_inverse = msg
            .is_inverse
            .ok_or_else(|| anyhow::anyhow!("Missing is_inverse for {}", msg.symbol))?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Skip/catch the failed conversion for that symbol and continue processing other instruments.
  2. Restrict the instrument subscription to symbols with complete definitions.
  3. Merge partial WS updates into a cached full instrument snapshot instead of converting each update standalone.
  4. Check whether the symbol is an index/composite that legitimately lacks root_symbol and handle it separately.

Example fix

// before
let instrument = BitmexInstrumentMsg::try_from(msg)?;
// after
match BitmexInstrumentMsg::try_from(msg) {
    Ok(i) => handle(i),
    Err(e) => log::debug!("Skipping incomplete instrument msg: {e}"),
}
Defensive patterns

Strategy: fallback

Validate before calling

if msg.root_symbol.is_none() { /* skip or fetch full snapshot via REST */ }

Type guard

fn complete_instrument(msg: &BitmexInstrumentMsg) -> bool {
    msg.root_symbol.is_some() && msg.underlying.is_some() && msg.quote_currency.is_some()
}

Try / catch

match TryInto::<InstrumentDef>::try_into(msg) {
    Ok(def) => apply(def),
    Err(e) => log::debug!("partial instrument update ignored: {e}"),
}

Prevention

When it happens

Trigger: Receiving an `instrument` WS message for a symbol whose root_symbol is null — e.g. composite/index symbols, delisted instruments, or BitMEX emitting partial instrument snapshots.

Common situations: Subscribing to the full instrument table where some rows (indices, .XBT symbols) lack root_symbol; BitMEX partial updates carrying only changed fields.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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