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

  1. Retry the query after a short delay; a transient exchange-side glitch usually resolves itself.
  2. Verify the HTTP client (or test stub) actually forwards from_id to Binance rather than returning a cached page.
  3. Check for a proxy/CDN caching layer on api.binance.com responses and disable caching.
  4. Upgrade the nautilus_binance adapter, in case the pagination logic has been fixed.

Example fix

// before
let page = cached_client.query_user_trades(&params).await?;
// after
let page = self.http_client.query_user_trades(&params).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

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


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)