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
- Re-run the pagination with an end time / snapshot pinned to a moment in the past so the dataset is immutable during the fetch.
- Retry the full pagination; transient concurrent writes may have resolved.
- Narrow the query window to reduce the chance of concurrent fills landing inside it.
- 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
- Always pin an end timestamp on fills queries over live data.
- Pause or sequence fills syncs relative to reconciliation jobs.
- Keep query windows short to reduce mutation exposure.
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
- AX fills pagination returned more unique rows
- AX fills pagination returned
- AX fills applied limit must be between 0 and
- AX fills page length
- AX fills pagination returned duplicate trade ID
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)