nautechsystems/nautilus_trader · error
Missing tick_size for {}
Error message
Missing tick_size for {} What it means
The instrument conversion guard chain continues: `tick_size` is null in the BitmexInstrumentMsg. Tick size is mandatory for constructing a valid instrument (price precision/step), so the conversion fails with this error.
Source
Thrown at crates/adapters/bitmex/src/websocket/messages.rs:591
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);
// Parse instrument type - default to PerpetualContract if not present
let instrument_type = msgView on GitHub (pinned to 18893faf8b)
Solutions
- Skip the incomplete record and rely on a later complete snapshot.
- Cache full instrument definitions and merge deltas before conversion.
- Restrict subscription to symbols known to have complete metadata.
- Refetch the instrument definition via REST if the WS update is incomplete.
Example fix
// before
let tick_size = msg.tick_size.ok_or_else(|| anyhow::anyhow!("Missing tick_size for {}", msg.symbol))?;
// after
let Some(tick_size) = msg.tick_size else {
log::debug!("Deferring {}: no tick_size yet", msg.symbol);
return Ok(());
}; Defensive patterns
Strategy: fallback
Validate before calling
if msg.tick_size.is_none() { /* wait for complete snapshot or fetch via REST */ } Type guard
fn has_tick_size(msg: &BitmexInstrumentMsg) -> Option<f64> { msg.tick_size } Try / catch
let Ok(def) = TryInto::<InstrumentDef>::try_into(msg) else {
pending.insert(msg.symbol.clone(), msg);
return Ok(());
}; Prevention
- Buffer incomplete rows until a later update completes their metadata.
- Seed instrument definitions from the REST /instrument endpoint before WS streaming.
- Never construct trading instruments without a verified tick_size.
When it happens
Trigger: WS instrument message arriving with tick_size: null — partial instrument updates, index symbols, or contracts where BitMEX has not published tick metadata.
Common situations: Initial bulk snapshot fetching where some rows are placeholders; partial delta updates missing fields not changed; newly listed instruments with incomplete metadata.
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
- Missing root_symbol for {}
- Missing underlying for {}
- Missing quote_currency for {}
- Missing multiplier for {}
- Missing is_quanto for {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/bea28bbc8b5ea26e.
Report an issue: GitHub.