nautechsystems/nautilus_trader · error · anyhow::Error

AX fills total_count must be non-negative, was

Error message

AX fills total_count must be non-negative, was {total_count}

What it means

When the AX fills response includes `total_count`, the client requires it to be non-negative before using it as the pagination denominator. A negative total_count is a malformed server response and would make the consistency checks (equality with accumulated rows) meaningless, so the fetch is aborted.

Solutions

  1. Dump the raw response JSON and confirm the negative total_count; if it's a sentinel, the server should omit the field instead.
  2. Report the malformed total_count to the AX service team; the client cannot paginate reliably without a valid count.
  3. Retry without expecting total_count (server omitting the field skips this check) once the server bug is fixed.
Defensive patterns

Strategy: try-catch

Try / catch

match client.paginate_fills(params).await {
    Ok(fills) => apply(fills),
    Err(e) if e.to_string().contains("total_count must be non-negative") => {
        error!("AX returned malformed total_count; aborting fills sync");
        Err(e)
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A fills page response whose `total_count` field is negative (e.g. -1) — typically a server sentinel or serialization bug — passed to the fills paginator.

Common situations: Backend uses -1 as a 'count unknown' sentinel; integer underflow in the AX service counter; corrupted JSON numeric field produced by a proxy or middleware.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

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

            anyhow::ensure!(
                page_len <= PAGE_SIZE as usize,
                "AX fills page length {page_len} exceeds requested limit {PAGE_SIZE}"
            );

            if let Some(limit) = response.limit {
                anyhow::ensure!(
                    (0..=PAGE_SIZE).contains(&limit),
                    "AX fills applied limit must be between 0 and {PAGE_SIZE}, was {limit}"
                );
                anyhow::ensure!(
                    page_len <= limit as usize,
                    "AX fills page length {page_len} exceeds applied limit {limit}"
                );
            }

            if let Some(total_count) = response.total_count {
                anyhow::ensure!(
                    total_count >= 0,
                    "AX fills total_count must be non-negative, was {total_count}"
                );

                if let Some(expected) = expected_total {
                    anyhow::ensure!(
                        total_count == expected,
                        "AX fills total_count changed during pagination: expected {expected}, was {total_count}"
                    );
                } else {
                    expected_total = Some(total_count);
                }
            }

            for fill in response.fills {
                anyhow::ensure!(
                    seen_trade_ids.insert(fill.trade_id.clone()),
                    "AX fills pagination returned duplicate trade ID {}",

View on GitHub (pinned to 18893faf8b)