nautechsystems/nautilus_trader · error · anyhow::Error

Failed to parse fill_sz='{}': {e}

Error message

Failed to parse fill_sz='{}': {e}

What it means

For a regular OKX order fill, if the incremental fillSz is present and non-zero, it is used directly as the fill quantity. This error is thrown when parse_quantity fails on that fill_sz (invalid number, negative, or exceeding the instrument's size precision), so the fill report cannot be built.

Source

Thrown at crates/adapters/okx/src/websocket/parse.rs:2107

        &msg.avg_px
    } else {
        &msg.px
    };
    let last_px = parse_price(price_str, price_precision).map_err(|e| {
        anyhow::anyhow!(
            "Failed to parse price (fill_px='{}', avg_px='{}', px='{}'): {}",
            msg.fill_px,
            msg.avg_px,
            msg.px,
            e
        )
    })?;

    // OKX provides fillSz (incremental fill) or accFillSz (cumulative total)
    // If fillSz is provided, use it directly as the incremental fill quantity
    let last_qty = if !msg.fill_sz.is_empty() && msg.fill_sz != "0" {
        parse_quantity(&msg.fill_sz, size_precision)
            .map_err(|e| anyhow::anyhow!("Failed to parse fill_sz='{}': {e}", msg.fill_sz,))?
    } else if let Some(ref acc_fill_sz) = msg.acc_fill_sz {
        // If fillSz is missing but accFillSz is available, calculate incremental fill
        if !acc_fill_sz.is_empty() && acc_fill_sz != "0" {
            let current_filled = parse_quantity(acc_fill_sz, size_precision).map_err(|e| {
                anyhow::anyhow!("Failed to parse acc_fill_sz='{acc_fill_sz}': {e}",)
            })?;

            // Calculate incremental fill as: current_total - previous_total
            if let Some(prev_qty) = previous_filled_qty {
                if current_filled < prev_qty {
                    anyhow::bail!(
                        "Cumulative fill went backwards: acc_fill_sz='{acc_fill_sz}' < previous_filled_qty={prev_qty} \
                         (possible stale data after reconnect)"
                    );
                }
                let incremental = current_filled - prev_qty;
                if incremental.is_zero() {
                    log::debug!(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Refresh the instrument definition (lotSz) so size_precision fits the received fill_sz.
  2. Log the raw order message and verify fill_sz is numeric and non-negative.
  3. Resync fills via OKX REST (fills endpoint) if the stream data is suspect.
  4. Update adapter parsing if OKX changed the fillSz format.
Defensive patterns

Strategy: validation

Validate before calling

// Rust: pre-validate fill_sz against instrument precision
fn fill_sz_ok(fill_sz: &str, precision: u32) -> bool {
    !fill_sz.is_empty() && fill_sz != "0"
        && rust_decimal::Decimal::from_str(fill_sz).map(|d| d.is_sign_positive() && d.scale() as u32 <= precision).unwrap_or(false)
}

Try / catch

if let Err(e) = parse_fill_report(&msg, ...) {
    log::warn!("fill_sz parse failed for {}: {e}", msg.inst_id);
    resync_order_state(&msg.inst_id);
}

Prevention

When it happens

Trigger: parse_fill_report receives an order update where fill_sz is non-empty and != "0" but parse_quantity(&msg.fill_sz, size_precision) errors — e.g. fractional digits beyond lotSz precision, negative value, or non-numeric string.

Common situations: Stale cached instrument definition with outdated lotSz after an OKX instrument parameter change; unexpected fill_sz like '0.000000001' on instruments with coarse lot sizes; malformed payload from an API schema change.

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