{"record":{"id":"adc8aa59e5b324de","repo":"nautechsystems/nautilus_trader","slug":"ax-fills-total-count-must-be-non-negative-was-to","errorCode":null,"errorMessage":"AX fills total_count must be non-negative, was {total_count}","messagePattern":"AX fills total_count must be non-negative, was (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/architect_ax/src/http/client.rs","lineNumber":2256,"sourceCode":"\n            anyhow::ensure!(\n                page_len <= PAGE_SIZE as usize,\n                \"AX fills page length {page_len} exceeds requested limit {PAGE_SIZE}\"\n            );\n\n            if let Some(limit) = response.limit {\n                anyhow::ensure!(\n                    (0..=PAGE_SIZE).contains(&limit),\n                    \"AX fills applied limit must be between 0 and {PAGE_SIZE}, was {limit}\"\n                );\n                anyhow::ensure!(\n                    page_len <= limit as usize,\n                    \"AX fills page length {page_len} exceeds applied limit {limit}\"\n                );\n            }\n\n            if let Some(total_count) = response.total_count {\n                anyhow::ensure!(\n                    total_count >= 0,\n                    \"AX fills total_count must be non-negative, was {total_count}\"\n                );\n\n                if let Some(expected) = expected_total {\n                    anyhow::ensure!(\n                        total_count == expected,\n                        \"AX fills total_count changed during pagination: expected {expected}, was {total_count}\"\n                    );\n                } else {\n                    expected_total = Some(total_count);\n                }\n            }\n\n            for fill in response.fills {\n                anyhow::ensure!(\n                    seen_trade_ids.insert(fill.trade_id.clone()),\n                    \"AX fills pagination returned duplicate trade ID {}\",","sourceCodeStart":2238,"sourceCodeEnd":2274,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/architect_ax/src/http/client.rs#L2238-L2274","documentation":"While paginating GET /fills in request_fill_reports, the Architect HTTP client validates the server-supplied total_count on every page and requires it to be >= 0. A negative total_count means the server is reporting a corrupt or sentinel counter, so the adapter aborts the whole fills request instead of reconciling against garbage. This is a fail-fast contract guard, not a client-side data error.","triggerScenarios":"A /fills page (GET with cursor, limit=100, sort_ts=desc, 7-day bounded window) returns a JSON total_count of -1 or any negative value. Typical sources: a server-side 'unknown count' sentinel, an unsigned/signed encoding bug in the AX API, a mock/test server returning defaulted i64 values, or a proxy mangling the response body.","commonSituations":"Pointing the client at a stub/test harness that does not implement total_count; an AX API version bump that changed total_count semantics; gateway or recording middleware that re-serializes the payload incorrectly.","solutions":["Log or curl the raw /fills response body for the failing cursor and confirm the total_count field is actually negative","Check the AX API changelog/docs for whether total_count can be a sentinel (e.g. -1 for unknown) in the API version you target","If -1 is a legal 'unknown' marker, patch the response deserialization upstream of this guard (map negative to None) via a PR rather than weakening the ensure!","Retry the request once to rule out transient payload corruption from proxies"],"exampleFix":"// before (stub /fills response)\n{\"fills\": [...], \"next_cursor\": null, \"total_count\": -1}\n// after\n{\"fills\": [...], \"next_cursor\": null, \"total_count\": 42}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match client.request_fill_reports(account_id, start, end).await {\n    Ok(reports) => { /* reconcile */ }\n    Err(e) if e.to_string().contains(\"total_count must be non-negative\") => {\n        log::error!(\"AX returned negative total_count; retrying once: {e}\");\n        // single retry with backoff; escalate if it repeats\n        tokio::time::sleep(Duration::from_secs(2)).await;\n        client.request_fill_reports(account_id, start, end).await?;\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Add a contract test that asserts the /fills schema (total_count non-negative, limit bounds, cursor shape) against the AX version you run","Pin adapter and AX API versions together and re-run the contract test on upgrades","Route the client through a response logger in staging so corrupt counters are visible before production reconciliation"],"tags":["architect-ax","fills","pagination","data-validation","rest-api"],"backgroundTag":"pagination-invariant-violation","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}