nautechsystems/nautilus_trader · error · anyhow::Error
AX fills pagination returned
Error message
AX fills pagination returned {} unique rows, expected {total_count} What it means
At the end of pagination, if the server reported a total_count, the number of unique fills collected across all pages must equal it exactly. A shortfall means pages were missed or rows vanished mid-read, so the client aborts instead of returning an incomplete fill list.
Solutions
- Re-run the full pagination and compare; a transient concurrent mutation usually resolves.
- Pin the query to a bounded, past time window so rows cannot disappear mid-read.
- Log per-page lengths and the total_count to identify which page lost rows, then report to the AX backend team.
- If fills are mutable by design, switch to an incremental/streaming fills feed rather than snapshot pagination.
Example fix
// before: live window, rows can vanish mid-pagination
let params = FillsParams { start: Some(start), ..Default::default() };
// after: closed snapshot window
let params = FillsParams { start: Some(start), end: Some(end), ..Default::default() }; Defensive patterns
Strategy: retry
Try / catch
match client.paginate_fills(params).await {
Ok(fills) => apply(fills),
Err(e) if e.to_string().contains("expected {total_count}") || e.to_string().contains("unique rows, expected") => {
warn!("incomplete fills snapshot; retrying once");
client.paginate_fills(params).await
}
Err(e) => return Err(e),
} Prevention
- Paginate over closed, past time windows to keep the row set immutable.
- Avoid running fills pagination concurrently with deletes/reconciliation.
- Log per-page counts to diagnose shortfalls quickly.
When it happens
Trigger: Pagination loop ended (next_cursor None) with fewer unique fills than the total_count reported on the first page — e.g. a page was truncated, or fills were deleted between page requests.
Common situations: Concurrent deletes/reconciliation removing fills while reading; a silently truncated final page; server counts rows before a filter the client-side window applies.
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 total_count changed during pagination: expected
- 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/eb56501c9fc1bd52.
Report an issue: GitHub.
Appendix: source
Thrown at crates/adapters/architect_ax/src/http/client.rs:2295
!next_cursor.is_empty(),
"AX fills returned an empty next_cursor"
);
anyhow::ensure!(
page_len > 0,
"AX fills returned an empty page with a next_cursor"
);
anyhow::ensure!(
seen_cursors.insert(next_cursor.clone()),
"AX fills pagination repeated cursor {next_cursor:?}"
);
params.cursor = Some(next_cursor);
}
None => break,
}
}
if let Some(total_count) = expected_total {
anyhow::ensure!(
fills.len() as i64 == total_count,
"AX fills pagination returned {} unique rows, expected {total_count}",
fills.len()
);
}
let ts_init = self.generate_ts_init();
let mut reports = Vec::with_capacity(fills.len());
for fill in &fills {
let instrument = self.resolve_report_instrument(fill.symbol).await?;
let report = parse_fill_report(fill, account_id, &instrument, ts_init)
.with_context(|| format!("Failed to parse AX fill {}", fill.trade_id))?;
reports.push(report);
}
Ok(reports)
}View on GitHub (pinned to 18893faf8b)