nautechsystems/nautilus_trader · error

Binance Spot account-trades pagination made no progress

Error message

Binance Spot account-trades pagination made no progress

What it means

When paginating account trades by venue order id, the loop advances fromId = max_trade_id + 1 after each full ACCOUNT_TRADES_PAGE_LIMIT page. If the next cursor is not greater than the current from_id the loop would spin forever, so the adapter bails with this no-progress guard (spot/execution.rs:1238).

Source

Thrown at crates/adapters/binance/src/spot/execution.rs:1238

                    page.iter().any(|report| report_time_ms(report) > end_time)
                });

                reports.extend(page.into_iter().filter(|report| {
                    requested_start_time
                        .is_none_or(|start_time| report_time_ms(report) >= start_time)
                        && requested_end_time
                            .is_none_or(|end_time| report_time_ms(report) <= end_time)
                        && seen_trade_ids.insert(report.trade_id)
                }));

                if page_len < ACCOUNT_TRADES_PAGE_LIMIT as usize || passed_end {
                    break;
                }

                let next_from_id = max_trade_id
                    .checked_add(1)
                    .context("Binance Spot trade ID overflow during pagination")?;
                anyhow::ensure!(
                    next_from_id > from_id,
                    "Binance Spot account-trades pagination made no progress"
                );
                from_id = next_from_id;
            }
        } else if let Some(query_start_time) = requested_start_time {
            let query_end_time = requested_end_time.unwrap_or_else(|| {
                self.clock.get_time_ns().as_i64() / NANOSECONDS_IN_MILLISECOND as i64
            });
            anyhow::ensure!(
                query_start_time <= query_end_time,
                "fill report start time must not exceed end time"
            );
            let mut window_start = query_start_time;

            loop {
                let window_end = window_start
                    .saturating_add(ACCOUNT_TRADES_MAX_INTERVAL_MS)

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Re-run the fill report with a narrower time window or after new trades exist
  2. Avoid issuing overlapping fill-report scans concurrently; keep a watermark of the last seen trade time
  3. If it persists, capture the raw myTrades response for the fromId range and report it to the NautilusTrader maintainers
Defensive patterns

Strategy: retry

Validate before calling

// keep a watermark so successive fill-report scans never rescan fully-seen pages
let start = last_fill_scan_ts.map(|t| t + 1.nanos()); // advance strictly

Try / catch

match generate_fill_reports(cmd).await {
    Err(e) if e.to_string().contains("pagination made no progress") => {
        // narrow the window and retry once; if it repeats, capture raw pages and report upstream
    }
    other => other?,
}

Prevention

When it happens

Trigger: Binance keeps returning full pages while the local filters (seen_trade_ids dedupe and the requested time window) discard every row, so max_trade_id never advances past the cursor — anomalous API behavior or a pathological window/dedupe interaction.

Common situations: Repeatedly running fill-report scans over the same window so every trade is already deduped; venue-side behavior changes returning unfiltered data outside the requested window; concurrent overlapping report generations.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/ec62e480f188d8f8. Report an issue: GitHub.