nautechsystems/nautilus_trader · error · anyhow::Error

AX orders pagination returned duplicate order ID {}

Error message

AX orders pagination returned duplicate order ID {}

What it means

Cursor-paginated AX orders fetch deduplicates order IDs across pages with a seen-set; a repeated oid means pages overlap and the caller would see the same order twice. The client aborts to protect the consumer from duplicated order data.

Source

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

        let mut params = GetOrdersParams {
            start_timestamp_ns: start.map(|timestamp| timestamp.as_i64()),
            end_timestamp_ns: end.map(|timestamp| timestamp.as_i64()),
            limit: Some(PAGE_SIZE),
            ..Default::default()
        };
        let mut orders = Vec::new();
        let mut seen_cursors = HashSet::new();
        let mut seen_order_ids = HashSet::new();

        loop {
            let response = self
                .inner
                .get_orders(&params)
                .await
                .map_err(|e| anyhow::anyhow!(e))?;

            for order in response.orders {
                anyhow::ensure!(
                    seen_order_ids.insert(order.oid.clone()),
                    "AX orders pagination returned duplicate order ID {}",
                    order.oid
                );
                orders.push(order);
            }

            match response.next_cursor {
                Some(next_cursor) => {
                    anyhow::ensure!(
                        seen_cursors.insert(next_cursor.clone()),
                        "AX orders pagination repeated cursor {next_cursor:?}"
                    );
                    params.cursor = Some(next_cursor);
                }
                None => break,
            }
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Restart pagination from the first page to get a clean snapshot
  2. Check that cursors are passed through unmodified (encoding/truncation by the adapter would replay pages)
  3. Increase page size to reduce the number of cursors and overlap opportunities
  4. Report overlapping cursors to AX/adapter maintainers
Defensive patterns

Strategy: retry

Validate before calling

let mut seen = std::collections::HashSet::new();
for page in pages {
    for o in &page.orders {
        if !seen.insert(o.oid.clone()) {
            return Err(anyhow!("overlapping pages: duplicate order {}", o.oid));
        }
    }
}

Try / catch

match result {
    Err(e) if e.to_string().contains("duplicate order ID") => restart_cursor_pagination(),
    Err(e) => return Err(e),
    Ok(orders) => use(orders),
}

Prevention

When it happens

Trigger: The server's cursor semantics break: two different cursors return overlapping order sets, e.g. new orders inserted shifting an unstable sort between cursor snapshots.

Common situations: Order inserts mid-pagination reordering results; venue cursor implementation regression; adapter sending malformed cursor values the server treats as the first page.

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/12690f6fdc63eb55. Report an issue: GitHub.