nautechsystems/nautilus_trader · error

Order in list denied: duplicate {}

Error message

Order in list denied: duplicate {}

What it means

While registering the list's orders, submit_order_list checks each client_order_id against the cache; if an order with that ID already exists it bails with 'Order in list denied: duplicate {id}'. Client order IDs must be unique within the cache.

Source

Thrown at crates/trading/src/strategy/mod.rs:277

            anyhow::bail!("OrderList denied: {e}");
        }

        {
            let cache_rc = core.cache_rc();
            let mut cache = cache_rc.try_borrow_mut().map_err(|_| {
                anyhow::anyhow!(
                    "Cannot submit order list {}: cache is currently borrowed",
                    order_list.id
                )
            })?;

            if cache.order_list_exists(&order_list.id) {
                anyhow::bail!("OrderList denied: duplicate {}", order_list.id);
            }

            for order in &orders {
                if cache.order_exists(&order.client_order_id()) {
                    anyhow::bail!(
                        "Order in list denied: duplicate {}",
                        order.client_order_id()
                    );
                }
            }

            cache.add_order_list(order_list.clone())?;
            for order in &orders {
                cache.add_order(order.clone(), position_id, client_id, true)?;
            }
        }

        for order in &orders {
            publish_order_initialized(order);
        }

        let params = params.filter(|params| !params.is_empty());

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Rely on the order factory to generate unique client order IDs; avoid hand-assigning them.
  2. Check cache.order_exists(client_order_id) before submitting each order.
  3. On restart/replay, seed the factory so IDs continue after the last used value rather than restarting.

Example fix

// before
order = self.order_factory.limit(..., client_order_id=ClientId("fixed-1"))

// after
if not self.cache.order_exists(client_order_id):
    order = self.order_factory.limit(...)  # factory-generated unique ID
Defensive patterns

Strategy: validation

Validate before calling

dupes = [o.client_order_id for o in orders if self.cache.order_exists(o.client_order_id)]
if dupes:
    raise ValueError(f"client order IDs already in cache: {dupes}")

Type guard

def all_new_orders(cache, orders) -> bool:
    return not any(cache.order_exists(o.client_order_id) for o in orders)

Try / catch

try:
    self.submit_order_list(order_list)
except RuntimeError as e:
    if "Order in list denied: duplicate" in str(e):
        self.log.error(f"client order ID collision: {e}")
    else:
        raise

Prevention

When it happens

Trigger: Any order in the list has a client_order_id already present in the cache — resubmitting submitted orders, or hand-built orders with fixed/colliding client order IDs.

Common situations: Custom client order ID generation that collides across restarts; replaying a session with persisted orders; reusing order factory state so IDs repeat.

Related errors


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