nautechsystems/nautilus_trader · error

Cumulative fill went backwards: acc_fill_sz='{acc_fill_sz}'

Error message

Cumulative fill went backwards: acc_fill_sz='{acc_fill_sz}' < previous_filled_qty={prev_qty} (possible stale data after reconnect)

What it means

Regular order fill parsing also treats acc_fill_sz as a monotonically increasing cumulative total. An update whose cumulative value is below the previously observed filled quantity is rejected as stale data (commonly replayed after reconnect) rather than applied, protecting order state from regression.

Source

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

        )
    })?;

    // 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!(
                        "Skipping duplicate fill: acc_fill_sz='{acc_fill_sz}' unchanged from previous={prev_qty}"
                    );
                    return Ok(None);
                }
                incremental
            } else {
                // First fill, use accumulated as incremental
                current_filled
            }
        } else {
            anyhow::bail!(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check for duplicate/overlapping subscriptions and ensure the reconnect handler re-syncs state via REST before resuming the stream
  2. Catch this error and drop the stale update if out-of-order delivery is expected in your deployment
  3. Track a sequence/timestamp per order and ignore updates older than the last applied one

Example fix

// before
let report = parse_fill_report(&msg, instrument, Some(prev_qty), ...)?; // bails on regression
// after
match parse_fill_report(&msg, instrument, Some(prev_qty), ...) {
    Ok(r) => handle(r),
    Err(e) if e.to_string().contains("went backwards") => tracing::warn!("stale fill ignored"),
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: try-catch

Validate before calling

if let (Some(prev), Some(acc)) = (previous_filled_qty, &msg.acc_fill_sz) {
    let cur: u64 = acc.parse().unwrap_or(0);
    if cur < prev.raw() { /* stale: drop or resync via REST */ }
}

Try / catch

match parse_fill_report(&msg, instrument, prev_qty, ...) {
    Ok(r) => handle(r),
    Err(e) if e.to_string().contains("went backwards") => tracing::warn!("stale fill dropped: {e}"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: An orders-channel update with acc_fill_sz less than the previously tracked filled quantity for the same order — usually an out-of-order or replayed message after a WebSocket reconnect.

Common situations: Reconnect replaying buffered messages; duplicate subscriptions delivering old snapshots; clock/sequence skew between REST snapshot and WS stream.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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