nautechsystems/nautilus_trader · error

retired Lighter binding conflicts at client_order_index {cli

Error message

retired Lighter binding conflicts at client_order_index {client_order_index} and venue order ID {venue_order_id}

What it means

When binding a brand-new (previously unseen cloid) terminal order, the code ensures no retired order is already bound to the same (client_order_index, venue_order_id) pair. A hit means two different cloids claim the same venue-side identity in the retired map — a hard conflict the adapter refuses to create.

Source

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

                self.mark_triggered_emitted(cloid);
            }
            return Ok(());
        }

        if let Some(existing) = self.retired_orders.identity_for_cloid(&cloid) {
            anyhow::ensure!(
                terminal
                    && existing.client_order_index == client_order_index
                    && existing.matches_venue_order_id(venue_order_id),
                "retired Lighter order {cloid} conflicts with reconciliation binding",
            );
            return Ok(());
        }

        let identity = OrderIdentity::restored(order, client_order_index, venue_order_id);

        if terminal {
            anyhow::ensure!(
                self.retired_orders
                    .cloid_for_venue(client_order_index, venue_order_id)
                    .is_none(),
                "retired Lighter binding conflicts at client_order_index {client_order_index} and venue order ID {venue_order_id}",
            );
            self.retired_orders.insert(cloid, identity);
        } 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);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Regenerate client order IDs so cloid↔client_order_index mapping is injective per session
  2. Clear retired_orders and rebuild from authoritative venue state
  3. Audit persisted order cache for duplicate (index, venue_id) pairs
Defensive patterns

Strategy: validation

Validate before calling

if retired.cloid_for_venue(idx, vid).is_some() {
    tracing::error!("venue identity {idx}/{vid} already bound to another cloid");
    full_reconcile_from_venue().await?;
}

Try / catch

if let Err(e) = bind_reconciliation(order, idx, vid, terminal).await {
    if e.to_string().contains("retired Lighter binding conflicts") {
        rebuild_retired_map_from_venue().await?;
        return Ok(());
    }
    return Err(e);
}

Prevention

When it happens

Trigger: A terminal reconciliation event whose cloid is new but whose client_order_index + venue_order_id combination is already occupied by a different cloid in retired_orders.

Common situations: Client_order_id collisions across restarts (same index reused with different cloids), venue reusing order IDs, corrupted persisted state.

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/8c3a9f48e0f195ad. Report an issue: GitHub.