{"record":{"id":"eb56501c9fc1bd52","repo":"nautechsystems/nautilus_trader","slug":"ax-fills-pagination-returned-unique-rows-expec","errorCode":null,"errorMessage":"AX fills pagination returned {} unique rows, expected {total_count}","messagePattern":"AX fills pagination returned (.+?) unique rows, expected (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/architect_ax/src/http/client.rs","lineNumber":2309,"sourceCode":"                        !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()\n            );\n        }\n\n        let ts_init = self.generate_ts_init();\n        let mut reports = Vec::with_capacity(fills.len());\n\n        for fill in &fills {\n            let instrument = self.resolve_report_instrument(fill.symbol).await?;\n            let report = parse_fill_report(fill, account_id, &instrument, ts_init)\n                .with_context(|| format!(\"Failed to parse AX fill {}\", fill.trade_id))?;\n            reports.push(report);\n        }\n\n        Ok(reports)\n    }","sourceCodeStart":2291,"sourceCodeEnd":2327,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/architect_ax/src/http/client.rs#L2291-L2327","documentation":"After the cursor chain terminates (next_cursor None), the client requires the total number of unique fills collected to equal the pinned total_count exactly. Fewer rows than promised means pages ended early, fills were removed mid-traversal, or the server's counter describes a different row set than the pages serve.","triggerScenarios":"Fills are corrected/restated (removed) during the traversal; the server's total_count is computed with different filter semantics (e.g. no time window) than the paged rows; the chain terminates prematurely due to a boundary bug; end=None races with the server clock.","commonSituations":"Historical reconciliation jobs on accounts with active corrections; API version drift changing what total_count counts; comparing adapter output against AX's own UI totals and hitting off-by-N rows on window edges.","solutions":["Retry with a fully historical, bounded window (fixed start/end in the past) that cannot gain or lose rows","Verify the window semantics: start inclusive vs exclusive relative to the server's counting, and both bounds within the 7-day cap","Cross-check the same window in the AX UI or an export to see whether the count or the rows are wrong","If the mismatch is deterministic on a frozen window, capture all pages with curl and report to AX — count and rows disagree server-side"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in 0..3 {\n    match client.request_fill_reports(account_id, Some(start), Some(end)).await {\n        Ok(reports) => return Ok(reports),\n        Err(e) if e.to_string().contains(\"unique rows, expected\") => {\n            log::warn!(\"fills row/total mismatch (attempt {}): {e}\", attempt + 1);\n            tokio::time::sleep(Duration::from_secs(2u64.pow(attempt))).await;\n        }\n        Err(e) => return Err(e.into()),\n    }\n}\nbail!(\"fills reconciliation could not obtain a consistent snapshot\");","preventionTips":["Use closed historical windows that cannot gain or lose rows during traversal","Cross-check adapter fill counts against AX's own totals during onboarding so semantic differences surface early","Fail reconciliation loudly — proceeding with a partial fill set silently corrupts PnL and position state"],"tags":["architect-ax","fills","pagination","count-mismatch","reconciliation"],"backgroundTag":"pagination-count-mismatch","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}