nautechsystems/nautilus_trader · error · anyhow::Error

AX fills pagination repeated cursor

Error message

AX fills pagination repeated cursor {next_cursor:?}

What it means

The paginator records every next_cursor it receives; if the server ever returns a cursor it already returned, pagination would cycle forever over the same rows. The client detects the repeat and aborts, both preventing an infinite loop and surfacing the server-side cursor bug.

Solutions

  1. Retry the pagination once; transient stalls may not recur.
  2. Check that fills have a unique, stable ordering key; report non-advancing cursors to the AX backend team with the repeated cursor value logged.
  3. Bound the loop client-side anyway (max pages) as a defensive habit, even though this check aborts cycles.
  4. Pin requests to one replica/endpoint to rule out divergent cursor state.

Example fix

// before: unbounded loop trusting the server to terminate
loop { let page = client.fills(¶ms).await?; /* ... */ }
// after: bound the iterations defensively
for _ in 0..MAX_PAGES { let page = client.fills(¶ms).await?; /* ... */ }
Defensive patterns

Strategy: try-catch

Try / catch

match client.paginate_fills(params).await {
    Ok(fills) => apply(fills),
    Err(e) if e.to_string().contains("repeated cursor") => {
        error!("AX cursor stalled; aborting this sync cycle and alerting");
        Err(e)
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: During fills pagination, a later page's next_cursor equals a cursor already used in an earlier request — the server failed to advance its cursor.

Common situations: Cursor computed from an unstable sort key (e.g. non-unique timestamp) causing the server to stall at the same position; backend deploy changed cursor encoding; load-balanced replicas returning divergent cursors.

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/d4e2f6fe4dcc25b0. Report an issue: GitHub.

Appendix: source

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

            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 18893faf8b)