nautechsystems/nautilus_trader · error · anyhow::Error
AX fills pagination returned duplicate trade ID
Error message
AX fills pagination returned duplicate trade ID {} What it means
The paginator tracks trade IDs it has already seen across all pages via a HashSet; if the same trade_id appears twice, the server is re-serving rows (bad cursor advancement or overlap) and the client aborts to prevent duplicated fills in the result.
Solutions
- Retry the whole pagination from scratch; a transient replica mismatch often resolves.
- Check whether a proxy/LB is serving inconsistent replicas and pin to a single endpoint.
- Compare the raw JSON of the previous and current pages to confirm the overlapping trade IDs and report to the AX backend team.
- Upgrade the client in case cursor-advance semantics changed server-side.
Defensive patterns
Strategy: try-catch
Try / catch
match client.paginate_fills(params).await {
Ok(fills) => apply(fills),
Err(e) if e.to_string().contains("duplicate trade ID") => {
warn!("AX served overlapping pages; restarting pagination");
client.paginate_fills(params).await
}
Err(e) => return Err(e),
} Prevention
- Deduplicate by trade_id downstream as an extra safety net.
- Pin fills reads to a single endpoint/replica where possible.
- Watch for backend deploys that change cursor semantics.
When it happens
Trigger: Any fills page iteration where a response contains a fill whose trade_id was already collected from an earlier page — duplicate delivery caused by server-side cursor overlap or a cursor that advanced incorrectly.
Common situations: Server cursor semantics changed after a backend deploy; a load balancer routes page requests to different replicas with divergent snapshots; retry logic re-delivers the tail of the previous page.
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 applied limit must be between 0 and
- AX fills page length
- AX fills pagination returned more unique rows
- AX fills pagination returned
- AX fills returned an empty next_cursor
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/69d3a09328052056.
Report an issue: GitHub.
Appendix: source
Thrown at crates/adapters/architect_ax/src/http/client.rs:2258
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 {
anyhow::ensure!(
fills.len() as i64 <= total_count,
"AX fills pagination returned more unique rows ({}) than total_count {total_count}",
fills.len()
);
}
match response.next_cursor {
Some(next_cursor) => {
anyhow::ensure!(View on GitHub (pinned to 18893faf8b)