{"record":{"id":"5ae29876b69ac2cc","repo":"nautechsystems/nautilus_trader","slug":"ax-fills-returned-an-empty-page-with-a-next-cursor","errorCode":null,"errorMessage":"AX fills returned an empty page with a next_cursor","messagePattern":"AX fills returned an empty page with a next_cursor","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/architect_ax/src/http/client.rs","lineNumber":2280,"sourceCode":"                );\n                fills.push(fill);\n            }\n\n            if let Some(total_count) = expected_total {\n                anyhow::ensure!(\n                    fills.len() as i64 <= total_count,\n                    \"AX fills pagination returned more unique rows ({}) than total_count {total_count}\",\n                    fills.len()\n                );\n            }\n\n            match response.next_cursor {\n                Some(next_cursor) => {\n                    anyhow::ensure!(\n                        !next_cursor.is_empty(),\n                        \"AX fills returned an empty next_cursor\"\n                    );\n                    anyhow::ensure!(\n                        page_len > 0,\n                        \"AX fills returned an empty page with a next_cursor\"\n                    );\n                    anyhow::ensure!(\n                        seen_cursors.insert(next_cursor.clone()),\n                        \"AX fills pagination repeated cursor {next_cursor:?}\"\n                    );\n                    params.cursor = Some(next_cursor);\n                }\n                None => break,\n            }\n        }\n\n        if let Some(total_count) = expected_total {\n            anyhow::ensure!(\n                fills.len() as i64 == total_count,\n                \"AX fills pagination returned {} unique rows, expected {total_count}\",\n                fills.len()","sourceCodeStart":2262,"sourceCodeEnd":2298,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/architect_ax/src/http/client.rs#L2262-L2298","documentation":"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.","triggerScenarios":"A fills page response with `fills: []` (page_len == 0) but a non-empty next_cursor present.","commonSituations":"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.","solutions":["Retry the pagination; if a concurrent deletion removed the rows, a fresh run may see a consistent view.","Narrow the query window or add filters so cursors stay anchored to stable rows.","Capture request+response and report the empty-page-with-cursor behavior to the AX backend team.","If rows are being deleted during reads, paginate over an immutable snapshot window instead."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match client.paginate_fills(params).await {\n    Ok(fills) => apply(fills),\n    Err(e) if e.to_string().contains(\"empty page with a next_cursor\") => {\n        warn!(\"AX empty page + cursor; retrying pagination\");\n        client.paginate_fills(params).await\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Avoid querying ranges where fills are being deleted concurrently.","Prefer snapshot windows over live tails for pagination.","Capture request params when reporting empty-page bugs."],"tags":["rust","pagination","cursor","http-client"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}