nautechsystems/nautilus_trader · error

Failed to parse liab '{liab_str}': {e}

Error message

Failed to parse liab '{liab_str}': {e}

What it means

parse_spot_margin_position_from_balance parses the OKX balance liability field (liab, or "0" when absent) into a Decimal. This error is raised when the liab string is not a valid decimal number, so the spot margin position cannot be derived from the balance record.

Source

Thrown at crates/adapters/okx/src/common/parse.rs:974

    account_id: AccountId,
    instrument_id: InstrumentId,
    size_precision: u8,
    ts_init: UnixNanos,
) -> anyhow::Result<Option<PositionStatusReport>> {
    // OKX returns empty strings for zero values, normalize to "0" before parsing
    let liab_str = if balance.liab.trim().is_empty() {
        "0"
    } else {
        balance.liab.trim()
    };
    let spot_in_use_str = if balance.spot_in_use_amt.trim().is_empty() {
        "0"
    } else {
        balance.spot_in_use_amt.trim()
    };

    let liab_dec = Decimal::from_str(liab_str)
        .map_err(|e| anyhow::anyhow!("Failed to parse liab '{liab_str}': {e}"))?;
    let spot_in_use_dec = Decimal::from_str(spot_in_use_str)
        .map_err(|e| anyhow::anyhow!("Failed to parse spotInUseAmt '{spot_in_use_str}': {e}"))?;

    // Skip if no margin position (no liability and no spot in use)
    if liab_dec.is_zero() && spot_in_use_dec.is_zero() {
        return Ok(None);
    }

    // Check if spotInUseAmt is zero first
    if spot_in_use_dec.is_zero() {
        // No position if spotInUseAmt is zero (regardless of liability)
        return Ok(None);
    }

    // Position side based on spotInUseAmt sign
    let (position_side, quantity_dec) = if spot_in_use_dec.is_sign_negative() {
        // Negative spotInUseAmt = sold (short position)
        (PositionSide::Short, spot_in_use_dec.abs())

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log the raw liab string from the balance payload for the failing currency
  2. Treat empty or missing liab as "0" before calling the parser (the code already defaults None to "0"; check empty strings too)
  3. Normalize/sanitize the balance record upstream (trim, strip separators) before parsing
  4. Upgrade the OKX adapter if a newer version handles the field format

Example fix

// before
let liab_dec = Decimal::from_str(liab_str).map_err(|e| anyhow!("Failed to parse liab '{liab_str}': {e}"))?;
// after
let liab_str = liab_str.trim();
let liab_dec = if liab_str.is_empty() { Decimal::ZERO } else { Decimal::from_str(liab_str).map_err(|e| anyhow!("Failed to parse liab '{liab_str}': {e}"))? };
Defensive patterns

Strategy: validation

Validate before calling

let liab = balance.liab.as_deref().unwrap_or("0").trim();
if liab.is_empty() || rust_decimal::Decimal::from_str(liab).is_err() { /* default to "0" or skip record */ }

Type guard

fn parseable_decimal(s: Option<&str>) -> bool {
    s.unwrap_or("0").trim().pipe(|t| !t.is_empty() && rust_decimal::Decimal::from_str(t).is_ok())
}

Try / catch

match parse_spot_margin_position_from_balance(&balance, ...) {
    Ok(Some(pos)) => handle(pos),
    Ok(None) => {},
    Err(e) if e.to_string().contains("Failed to parse liab") => log::warn!("bad liab '{}': {e}", balance.liab),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: balance.liab (or the liability currency field) contains an empty, non-numeric, or malformed string (e.g. "", "NaN", thousands separators) when parsing an OKX spot margin balance response.

Common situations: OKX returning empty liab for currencies with no position but unexpected formats; API schema changes; mocked/recorded fixtures with placeholder values; locale-formatted numbers from custom middleware.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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