nautechsystems/nautilus_trader · error

Missing quote_currency for {}

Error message

Missing quote_currency for {}

What it means

The instrument message conversion requires `quote_currency`, which arrived null in the BitmexInstrumentMsg. Without a quote currency no instrument can be defined, so the conversion aborts with this error naming the symbol.

Source

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

    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))?;

        // Parse state - default to Open if not present
        let state = msg
            .state
            .and_then(|s| serde_json::from_str::<BitmexInstrumentState>(&format!("\"{s}\"")).ok())
            .unwrap_or(BitmexInstrumentState::Open);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Skip the affected symbol; convert only messages with all required fields present.
  2. Maintain a snapshot cache so partial deltas never drive a full conversion.
  3. Exclude index/special symbols from instrument subscriptions.
  4. Verify BitMEX instrument schema and update adapter mappings if changed.

Example fix

// before
let def = BitmexInstrumentDef::try_from(msg).context("convert instrument")?;
// after
let Ok(def) = BitmexInstrumentDef::try_from(msg) else {
    log::debug!("Skipping instrument msg missing required fields");
    return Ok(());
};
Defensive patterns

Strategy: fallback

Validate before calling

if msg.quote_currency.is_none() { /* skip symbol */ }

Type guard

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

Try / catch

if let Err(e) = convert_instrument(&msg) {
    debug!("instrument {} skipped: {e}", msg.symbol);
}

Prevention

When it happens

Trigger: WS instrument message for a symbol with quote_currency: null — index symbols (e.g. .BVOL), partial snapshots, or malformed/delisted instrument rows.

Common situations: Subscribing to all instruments (indices and special symbols included); BitMEX schema changes; cached definitions not merged with partial delta updates.

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/7d9cf8f2cd03b48f. Report an issue: GitHub.