nautechsystems/nautilus_trader · error

Venue order ID counter exhausted

Error message

Venue order ID counter exhausted

What it means

The matching engine's venue order ID generator maintains an internal u64/i128-style counter used to build venue order IDs of the form {venue}-{raw_id}-{count}. When incrementing the counter with checked_add overflows the numeric type, generation fails with this error, preventing silent ID reuse or wraparound.

Source

Thrown at crates/execution/src/matching_engine/ids_generator.rs:265

            ))
        }
    }

    /// Generates a venue order ID.
    ///
    /// # Panics
    ///
    /// Panics if the deterministic order counter is exhausted.
    pub fn generate_venue_order_id(&mut self) -> VenueOrderId {
        self.try_generate_venue_order_id()
            .expect("Venue order ID counter exhausted")
    }

    fn try_generate_venue_order_id(&mut self) -> anyhow::Result<VenueOrderId> {
        self.order_count = self
            .order_count
            .checked_add(1)
            .ok_or_else(|| anyhow::anyhow!("Venue order ID counter exhausted"))?;

        if self.use_random_ids {
            Ok(VenueOrderId::new(UUID4::new().to_string()))
        } else {
            Ok(VenueOrderId::new(
                format!("{}-{}-{}", self.venue, self.raw_id, self.order_count).as_str(),
            ))
        }
    }
}

fn fnv1a_trade_id_hash(venue: Venue, raw_id: u32, ts_init_ns: u64) -> u64 {
    let mut hash: u64 = FNV_OFFSET_BASIS;

    for bytes in [
        venue.as_str().as_bytes(),
        b"\x1f",
        &raw_id.to_le_bytes(),

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Restart or recreate the matching engine to reset the order ID counter at a safe baseline.
  2. Enable use_random_ids (UUID-based venue order IDs) so numeric counter capacity is irrelevant.
  3. Reduce order volume per engine instance or shard the run across multiple engines with distinct raw_id values.
  4. Upgrade to a build where the counter uses a wider integer type if your workload legitimately exceeds the current width.

Example fix

// before: sequential IDs with huge volume, counter eventually overflows
let gen = OrderIdsGenerator::new(venue, raw_id, false /* use_random_ids */);
// after: UUID-based IDs avoid counter exhaustion
let gen = OrderIdsGenerator::new(venue, raw_id, true /* use_random_ids */);
Defensive patterns

Strategy: validation

Validate before calling

// estimate whether the run can exceed counter capacity before starting
let expected_orders: u128 = estimated_orders(run_config);
assert!(expected_orders < i64::MAX as u128,
    "run would exhaust the venue order ID counter; use random IDs or shard");

Try / catch

match ids_generator.get_venue_order_id() {
    Err(e) if e.to_string().contains("counter exhausted") => {
        log::error!("order ID space exhausted, restarting generator: {e}");
        ids_generator.reset(); // or switch to use_random_ids and retry once
    }
    other => other,
}

Prevention

When it happens

Trigger: Generating more venue order IDs in one matching engine's lifetime than the counter type can represent — practically only via extremely long-running simulations, exhaustive backtests, or a misconfigured raw_id base pushing the counter near its maximum.

Common situations: Very large-scale backtests or market-replay runs generating billions of orders; a custom use_random_ids=false setup with a near-max initial raw_id; long-lived live matching engine instances never restarted.

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/81344ad99e312c28. Report an issue: GitHub.