nautechsystems/nautilus_trader · error

Cannot determine spread fill quantity: fill_sz='{}' and acc_

Error message

Cannot determine spread fill quantity: fill_sz='{}' and acc_fill_sz='{}'

What it means

To compute a spread fill quantity the update must carry either the incremental fill_sz or the cumulative acc_fill_sz. When both are missing/empty the parser cannot determine how much was filled and bails.

Source

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

                anyhow::bail!(
                    "Cumulative spread fill went backwards: acc_fill_sz='{}' < previous_filled_qty={} \
                     (possible stale data after reconnect)",
                    msg.acc_fill_sz,
                    prev_qty
                );
            }

            if (current_filled - prev_qty).is_zero() {
                log::debug!(
                    "Skipping duplicate spread fill: acc_fill_sz='{}' unchanged from previous={}",
                    msg.acc_fill_sz,
                    prev_qty
                );
                return Ok(None);
            }
        }
    } else {
        anyhow::bail!(
            "Cannot determine spread fill quantity: fill_sz='{}' and acc_fill_sz='{}'",
            msg.fill_sz,
            msg.acc_fill_sz
        );
    }

    anyhow::bail!(
        "missing fee for spread fill report sprd_id={}; OKX sprd-orders updates omit fee",
        msg.sprd_id
    )
}

/// Parses an OKX order message into a Nautilus fill report.
///
/// # Errors
///
/// Returns an error if order quantities, prices, or fees cannot be parsed.
pub fn parse_fill_report(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the raw message: route only sprd-orders updates with a fill state through parse_spread_order_fill_report
  2. Return a non-fill event (status update) when both size fields are absent instead of treating it as an error
  3. Verify against current OKX sprd-orders docs whether field semantics changed

Example fix

// before
anyhow::bail!("Cannot determine spread fill quantity: ...");
// after
if msg.fill_sz.is_none() && msg.acc_fill_sz.is_none() {
    return Ok(None); // not a fill, status-only update
}
Defensive patterns

Strategy: type-guard

Validate before calling

fn has_fill_qty(msg: &OKXSpreadOrderMsg) -> bool {
    msg.fill_sz.as_deref().map_or(false, |s| !s.is_empty() && s != "0")
        || msg.acc_fill_sz.as_deref().map_or(false, |s| !s.is_empty())
}

Type guard

fn is_spread_fill_update(msg: &OKXSpreadOrderMsg) -> bool { has_fill_qty(msg) }

Try / catch

if !has_fill_qty(&msg) { return Ok(None); } // status-only, not a fill

Prevention

When it happens

Trigger: A sprd-orders update arrives where fill_sz is empty/zero-valued and acc_fill_sz is None/empty — e.g. a pure status change (canceled, state update) being routed through the fill-report parser.

Common situations: OKX pushing non-fill spread order updates that the event router mistakenly treats as fills; API changes removing fields; partially populated test fixtures.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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