nautechsystems/nautilus_trader · error · anyhow::Error

AX fills total_count changed during pagination: expected

Error message

AX fills total_count changed during pagination: expected {expected}, was {total_count}

What it means

During multi-page AX fills pagination, the first page's total_count is captured and every later page must report the identical value. If the total changes mid-pagination, the result set mutated while being read (fills added or removed), so the client aborts rather than return an inconsistent snapshot.

Solutions

  1. Re-run the pagination with an end time / snapshot pinned to a moment in the past so the dataset is immutable during the fetch.
  2. Retry the full pagination; transient concurrent writes may have resolved.
  3. Narrow the query window to reduce the chance of concurrent fills landing inside it.
  4. If totals legitimately change constantly, use a streaming/incremental fills endpoint instead of snapshot pagination.

Example fix

// before: open-ended query, totals drift while paging
let params = FillsParams { start: Some(hour_ago), ..Default::default() };
// after: bound both ends so the snapshot is stable
let params = FillsParams { start: Some(hour_ago), end: Some(now), ..Default::default() };
Defensive patterns

Strategy: retry

Validate before calling

// use a closed time window so the dataset cannot grow during pagination
let params = FillsParams { start: Some(start), end: Some(end), ..Default::default() };

Try / catch

match client.paginate_fills(params).await {
    Ok(fills) => apply(fills),
    Err(e) if e.to_string().contains("total_count changed during pagination") => {
        info!("fills mutated mid-read; retrying snapshot");
        client.paginate_fills(params).await
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Paginating fills across two or more requests where a later page's `total_count` differs from the value captured on the first page — e.g. new fills arrived or old ones were amended/deleted between page fetches.

Common situations: Fetching fills over a live/unbounded time range while trading continues; a background reconciliation job mutates fills; querying without pinning an end timestamp.

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

Appendix: source

Thrown at crates/adapters/architect_ax/src/http/client.rs:2248

            if let Some(limit) = response.limit {
                anyhow::ensure!(
                    (0..=PAGE_SIZE).contains(&limit),
                    "AX fills applied limit must be between 0 and {PAGE_SIZE}, was {limit}"
                );
                anyhow::ensure!(
                    page_len <= limit as usize,
                    "AX fills page length {page_len} exceeds applied limit {limit}"
                );
            }

            if let Some(total_count) = response.total_count {
                anyhow::ensure!(
                    total_count >= 0,
                    "AX fills total_count must be non-negative, was {total_count}"
                );

                if let Some(expected) = expected_total {
                    anyhow::ensure!(
                        total_count == expected,
                        "AX fills total_count changed during pagination: expected {expected}, was {total_count}"
                    );
                } else {
                    expected_total = Some(total_count);
                }
            }

            for fill in response.fills {
                anyhow::ensure!(
                    seen_trade_ids.insert(fill.trade_id.clone()),
                    "AX fills pagination returned duplicate trade ID {}",
                    fill.trade_id
                );
                fills.push(fill);
            }

            if let Some(total_count) = expected_total {

View on GitHub (pinned to 18893faf8b)