nautechsystems/nautilus_trader · error · anyhow::Error

AX fills returned an empty page with a next_cursor

Error message

AX fills returned an empty page with a next_cursor

What it means

A page that carries a next_cursor must also contain at least one fill; an empty page plus a cursor would make the paginator request pages that yield nothing while appearing to continue, risking an infinite or useless loop. The client rejects this combination outright.

Solutions

  1. Retry the pagination; if a concurrent deletion removed the rows, a fresh run may see a consistent view.
  2. Narrow the query window or add filters so cursors stay anchored to stable rows.
  3. Capture request+response and report the empty-page-with-cursor behavior to the AX backend team.
  4. If rows are being deleted during reads, paginate over an immutable snapshot window instead.
Defensive patterns

Strategy: try-catch

Try / catch

match client.paginate_fills(params).await {
    Ok(fills) => apply(fills),
    Err(e) if e.to_string().contains("empty page with a next_cursor") => {
        warn!("AX empty page + cursor; retrying pagination");
        client.paginate_fills(params).await
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A fills page response with `fills: []` (page_len == 0) but a non-empty next_cursor present.

Common situations: Server pagination bug where the cursor points past rows the filter excludes (e.g. a state change removed fills under the cursor); offset/cursor drift after concurrent deletions.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/5ae29876b69ac2cc. Report an issue: GitHub.

Appendix: source

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

                );
                fills.push(fill);
            }

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

View on GitHub (pinned to 18893faf8b)