nautechsystems/nautilus_trader · error

active Lighter binding conflicts at client_order_index {clie

Error message

active Lighter binding conflicts at client_order_index {client_order_index}

What it means

Raised when binding a cloid to a client_order_index finds the index already occupied by a different cloid in the active-bindings map (dashmap Entry::Occupied). The dispatcher enforces a one-to-one mapping between client_order_index and cloid for active orders; a collision means duplicate or inconsistent order identity state.

Source

Thrown at crates/adapters/lighter/src/websocket/dispatch.rs:1367

        } else {
            if let Some(existing) = self.venue_id_map.get(&cloid) {
                anyhow::ensure!(
                    *existing.value() == venue_order_id,
                    "active Lighter order {cloid} conflicts with venue order ID {venue_order_id}",
                );
            }

            match self.cloid_map.entry(client_order_index) {
                dashmap::mapref::entry::Entry::Vacant(entry) => {
                    self.venue_id_map.insert(cloid, venue_order_id);
                    self.order_identities.insert(cloid, identity);
                    entry.insert(cloid);
                    if order.is_triggered() == Some(true) {
                        self.mark_triggered_emitted(cloid);
                    }
                }
                dashmap::mapref::entry::Entry::Occupied(_) => {
                    anyhow::bail!(
                        "active Lighter binding conflicts at client_order_index {client_order_index}",
                    );
                }
            }
        }
        Ok(())
    }

    /// Drop a cloid registration (called from the spawn's error branch when
    /// the tx never reaches the wire).
    pub(crate) fn forget_cloid(&self, index: i64) {
        self.cloid_map.remove(&index);
    }

    /// Resolve a venue client-order index across active and replay caches.
    pub(crate) fn resolve_client_order_index(&self, index: i64) -> Option<ClientOrderId> {
        self.cloid_map
            .get(&index)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Generate a fresh unique cloid/client_order_index for the retried order instead of reusing the old identity.
  2. Ensure a single owner (one execution client) assigns client_order_index values for the account.
  3. Clear or rebuild the binding map from a fresh reconciliation snapshot if it contains stale entries.
  4. Serialize order submissions (or guard the binding step) so concurrent tasks cannot claim the same index.
  5. Check for counter resets after restarts; persist or re-derive the next index from exchange state.

Example fix

// before: reusing the same index on retry
submit_order(order.with_client_order_index(prev_index)).await?;
// -> active Lighter binding conflicts at client_order_index 42

// after: allocate a new index per submission
let idx = next_unique_client_order_index();
submit_order(order.with_client_order_index(idx)).await?;
Defensive patterns

Strategy: validation

Validate before calling

// before submitting, allocate a guaranteed-unique index
let idx = next_unique_client_order_index(); // atomic counter per account

Try / catch

match submit_order(order).await {
    Err(e) if e.to_string().contains("binding conflicts") => {
        log::error!("duplicate order identity: {e:#}");
        // regenerate cloid + index and resubmit once
        let order = order.with_new_identity();
        submit_order(order).await?;
    }
    r => r?,
}

Prevention

When it happens

Trigger: Restoring/registering an order whose client_order_index is already bound to another cloid — e.g. two orders generated with the same index, a reconciliation replay re-inserting an existing binding, or concurrent submissions racing to claim the same index.

Common situations: Running multiple strategy instances that derive indices from a shared counter without coordination; re-sending an order after a timeout without generating a fresh cloid; a reconnect/reconcile path replaying bindings that are already present; clock/counter reset causing index reuse.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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