nautechsystems/nautilus_trader · error
`tick_sz` is empty for {}
Error message
`tick_sz` is empty for {} What it means
parse_spread_instrument validates an OKX spread instrument definition before constructing a Nautilus instrument. A spread definition with an empty `tick_sz` (minimum price increment) cannot produce a valid Price, so parsing is aborted with this message naming the `sprd_id`.
Source
Thrown at crates/adapters/okx/src/common/parse.rs:1645
/// Parses an OKX spread definition into a Nautilus crypto spread.
///
/// # Errors
///
/// Returns an error if the spread definition cannot be parsed.
///
/// # Panics
///
/// Panics if the constructed instrument fails validation.
pub fn parse_spread_instrument(
definition: &OKXSpread,
margin_init: Option<Decimal>,
margin_maint: Option<Decimal>,
maker_fee: Option<Decimal>,
taker_fee: Option<Decimal>,
ts_init: UnixNanos,
) -> anyhow::Result<InstrumentAny> {
if definition.tick_sz.is_empty() {
anyhow::bail!("`tick_sz` is empty for {}", definition.sprd_id);
}
if definition.lot_sz.is_empty() {
anyhow::bail!("`lot_sz` is empty for {}", definition.sprd_id);
}
let context = format!("SPREAD instrument {}", definition.sprd_id);
let instrument_id = parse_instrument_id(definition.sprd_id);
let raw_symbol = Symbol::from_ustr_unchecked(definition.sprd_id);
let underlying =
Currency::get_or_create_crypto_with_context(definition.base_ccy, Some(&context));
let quote_currency =
Currency::get_or_create_crypto_with_context(definition.quote_ccy, Some(&context));
let settlement_currency = spread_settlement_currency(definition, underlying, quote_currency);
let is_inverse = matches!(definition.sprd_type, OKXSpreadType::Inverse);
let activation_ns = definition
.list_time
.map(parse_millisecond_timestamp)View on GitHub (pinned to 18893faf8b)
Solutions
- Filter spread instrument records with an empty `tickSz` before parsing (skip or defer them)
- Re-fetch the spread instrument definition; the empty field may be transient for newly listed spreads
- Check the instrument state field (`state`/`listTime`) and only parse live instruments
Example fix
// before
for sprd in response.data {
let inst = parse_spread_instrument(&sprd, ...)?; // bails on empty tickSz
}
// after
for sprd in response.data.into_iter().filter(|s| !s.tick_sz.is_empty() && !s.lot_sz.is_empty()) {
let inst = parse_spread_instrument(&sprd, ...)?;
} Defensive patterns
Strategy: validation
Validate before calling
if definition.tick_sz.is_empty() || definition.lot_sz.is_empty() {
return Ok(None); // skip incomplete spread instrument
} Try / catch
match parse_spread_instrument(&definition, ...) {
Ok(inst) => add(inst),
Err(e) if e.to_string().contains("is empty") => log::debug!("skipped incomplete spread: {e}"),
Err(e) => return Err(e),
} Prevention
- Filter pre-open/incomplete spreads before parsing
- Check state/listTime on spread records
- Re-fetch definitions for newly listed spreads instead of failing the whole batch
When it happens
Trigger: Calling parse_spread_instrument (directly or via request_spread_instrument/request_spread_instruments) with an OKX spread instrument JSON whose `tickSz` field is an empty string — typically from a pre-listing or placeholder spread record returned by the spreads endpoint.
Common situations: Requesting all spread instruments and iterating over records without filtering pre-open ones; stale cached instrument data; OKX API returning partially populated definitions during new spread listings.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- `lot_sz` is empty for {}
- Empty underlying for {inst_id}: instrument may be pre-open o
- Unsupported bar specification for AX: {step}-{:?}
- AX requires whole contract quantities, was {}
- Order quantity must be at least 1 contract
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/170923f2089f0ac1.
Report an issue: GitHub.