nautechsystems/nautilus_trader · error · anyhow::Error
Binance user-trades pagination made no progress
Error message
Binance user-trades pagination made no progress
What it means
During time-windowed pagination of Binance user trades, the adapter advances a `from_id` cursor after each full page. Before advancing it asserts that the new cursor is strictly greater than the previous one; if the exchange keeps returning the same trade IDs the loop would spin forever, so it aborts with this error. It is an internal progress invariant guarding an API that is misbehaving or returning stale data.
Solutions
- Retry the query after a short delay; a transient exchange-side glitch usually resolves itself.
- Verify the HTTP client (or test stub) actually forwards from_id to Binance rather than returning a cached page.
- Check for a proxy/CDN caching layer on api.binance.com responses and disable caching.
- Upgrade the nautilus_binance adapter, in case the pagination logic has been fixed.
Example fix
// before let page = cached_client.query_user_trades(¶ms).await?; // after let page = self.http_client.query_user_trades(¶ms).await?; // bypass cache, pass from_id through
Defensive patterns
Strategy: retry
Try / catch
// Rust
match client.generate_mass_status().await {
Err(e) if e.to_string().contains("pagination made no progress") => {
warn!("transient Binance pagination glitch, retrying");
tokio::time::sleep(Duration::from_secs(2)).await;
client.generate_mass_status().await?;
}
r => r?,
} Prevention
- Do not put caching proxies in front of Binance REST endpoints.
- Use the real HTTP client (not a static stub) in tests for reconciliation paths.
- Keep the adapter updated for pagination fixes.
When it happens
Trigger: Calling generate_fill_reports (via generate_mass_status) where a full page (USER_TRADES_PAGE_LIMIT) of trades is returned whose maximum trade id plus one equals or is lower than the current from_id cursor — i.e. Binance returned overlapping/duplicate pages for the same cursor.
Common situations: Binance API anomalies such as cached or replayed responses behind a proxy, a mock/stub HTTP client returning the same fixture page regardless of from_id, or exchange-side ordering glitches during high trade volume.
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 open-orders applied limit must be between 0 and
- AX open-orders response offset mismatch: requested
- AX open-orders total_count must be non-negative, was
- Binance user trade ID overflow during pagination
- Either venue_order_id or client_order_id must be provided
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/4ea319c15ee24666.
Report an issue: GitHub.
Appendix: source
Thrown at crates/adapters/binance/src/futures/execution.rs:2514
let page_len = page.len();
let max_trade_id = page.iter().map(|trade| trade.id).max().unwrap();
let passed_window_end = page.iter().any(|trade| trade.time > window_end);
trades.extend(page.into_iter().filter(|trade| {
trade.time >= window_start
&& trade.time <= window_end
&& seen_trade_ids.insert(trade.id)
}));
if page_len < USER_TRADES_PAGE_LIMIT as usize || passed_window_end {
break;
}
let next_from_id = max_trade_id
.checked_add(1)
.context("Binance user trade ID overflow during pagination")?;
anyhow::ensure!(
from_id.is_none_or(|cursor| next_from_id > cursor),
"Binance user-trades pagination made no progress"
);
from_id = Some(next_from_id);
}
if window_end >= query_end_time {
break;
}
window_start = window_end.saturating_add(1);
}
} else {
anyhow::ensure!(
cmd.end.is_none(),
"Binance Futures fill report end time requires start time for a complete range"
);
let mut from_id = 0;
View on GitHub (pinned to 18893faf8b)