nautechsystems/nautilus_trader · error · anyhow::Error

AX fills pagination repeated cursor {next_cursor:?}

Error message

AX fills pagination repeated cursor {next_cursor:?}

What it means

Every accepted next_cursor is inserted into a seen_cursors HashSet; a cursor repeating within one traversal means the chain loops, and continuing would fetch the same pages forever. The guard converts that would-be infinite loop into an immediate error with the offending cursor value.

Source

Thrown at crates/adapters/architect_ax/src/http/client.rs:2298

            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!(
                        !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();

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Retry the full traversal — transient cursor-cache issues on the server often clear
  2. Capture the full cursor chain with curl and verify no legitimate loop exists in the raw responses
  3. Confirm the adapter version matches the AX API version you connect to (cursor formats change between versions)
  4. Report to AX with the chain if it loops deterministically — this cannot be worked around client-side
Defensive patterns

Strategy: try-catch

Try / catch

match client.request_fill_reports(account_id, start, end).await {
    Ok(reports) => Ok(reports),
    Err(e) if e.to_string().contains("repeated cursor") => {
        log::error!("AX cursor chain loops on {e}; aborting to avoid infinite pagination");
        Err(e) // never retry-loop here — the guard exists to stop a server-side loop
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: AX echoes the request cursor back when it has nothing new to serve; the cursor generation has a bug and re-issues a token for an already-served page; a page of exactly PAGE_SIZE rows triggers stale cursor regeneration at the boundary.

Common situations: Long-running reconciliation traversals that hit server cursor-cache expiry mid-chain; mismatched adapter/server API versions; paginating with parameters the server's cursor implementation does not support (custom sort).

Related errors


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