nautechsystems/nautilus_trader · error
`lot_sz` is empty for {}
Error message
`lot_sz` is empty for {} What it means
parse_spread_instrument also validates `lot_sz` (minimum tradable quantity increment). An empty value cannot be converted to a Quantity, so parsing bails with this message naming the `sprd_id`. This check runs immediately after the `tick_sz` check.
Source
Thrown at crates/adapters/okx/src/common/parse.rs:1649
/// 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)
.ok_or_else(|| anyhow::anyhow!("`list_time` is required for {}", definition.sprd_id))?;
let expiration_ns = definition
.exp_time
.map(parse_millisecond_timestamp)View on GitHub (pinned to 18893faf8b)
Solutions
- Filter out spread records with an empty `lotSz` (together with empty `tickSz`) before parsing
- Re-fetch the instrument definition from OKX and retry
- Only parse instruments whose state indicates they are live
- If a legitimately live instrument has empty lotSz, report/update the adapter — the API contract expects a populated lotSz
Defensive patterns
Strategy: validation
Validate before calling
if definition.lot_sz.is_empty() {
eprintln!("skipping spread {} with empty lot_sz", definition.sprd_id);
return Ok(None);
} Try / catch
match parse_spread_instrument(&definition, ...) {
Ok(inst) => add(inst),
Err(e) if e.to_string().contains("lot_sz") => log::debug!("skipped: {e}"),
Err(e) => return Err(e),
} Prevention
- Validate both tick_sz and lot_sz together before parsing
- Only parse spreads with state live
- Treat empty trading-parameter fields as a signal of pre-listing records
When it happens
Trigger: Calling parse_spread_instrument / request_spread_instruments with a spread definition whose `lotSz` is an empty string, typically for pre-open or placeholder spread instruments returned by OKX.
Common situations: Bulk-parsing all spreads without filtering incomplete records; instruments newly listed where OKX has not populated lot size yet; cached/malformed instrument snapshots.
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
- `tick_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/3c46b0974c0b3b4d.
Report an issue: GitHub.