{"record":{"id":"7deda50f55f6de1f","repo":"nautechsystems/nautilus_trader","slug":"active-lighter-binding-conflicts-at-client-order-i","errorCode":null,"errorMessage":"active Lighter binding conflicts at client_order_index {client_order_index}","messagePattern":"active Lighter binding conflicts at client_order_index (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/lighter/src/websocket/dispatch.rs","lineNumber":1367,"sourceCode":"        } else {\n            if let Some(existing) = self.venue_id_map.get(&cloid) {\n                anyhow::ensure!(\n                    *existing.value() == venue_order_id,\n                    \"active Lighter order {cloid} conflicts with venue order ID {venue_order_id}\",\n                );\n            }\n\n            match self.cloid_map.entry(client_order_index) {\n                dashmap::mapref::entry::Entry::Vacant(entry) => {\n                    self.venue_id_map.insert(cloid, venue_order_id);\n                    self.order_identities.insert(cloid, identity);\n                    entry.insert(cloid);\n                    if order.is_triggered() == Some(true) {\n                        self.mark_triggered_emitted(cloid);\n                    }\n                }\n                dashmap::mapref::entry::Entry::Occupied(_) => {\n                    anyhow::bail!(\n                        \"active Lighter binding conflicts at client_order_index {client_order_index}\",\n                    );\n                }\n            }\n        }\n        Ok(())\n    }\n\n    /// Drop a cloid registration (called from the spawn's error branch when\n    /// the tx never reaches the wire).\n    pub(crate) fn forget_cloid(&self, index: i64) {\n        self.cloid_map.remove(&index);\n    }\n\n    /// Resolve a venue client-order index across active and replay caches.\n    pub(crate) fn resolve_client_order_index(&self, index: i64) -> Option<ClientOrderId> {\n        self.cloid_map\n            .get(&index)","sourceCodeStart":1349,"sourceCodeEnd":1385,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/lighter/src/websocket/dispatch.rs#L1349-L1385","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Generate a fresh unique cloid/client_order_index for the retried order instead of reusing the old identity.","Ensure a single owner (one execution client) assigns client_order_index values for the account.","Clear or rebuild the binding map from a fresh reconciliation snapshot if it contains stale entries.","Serialize order submissions (or guard the binding step) so concurrent tasks cannot claim the same index.","Check for counter resets after restarts; persist or re-derive the next index from exchange state."],"exampleFix":"// before: reusing the same index on retry\nsubmit_order(order.with_client_order_index(prev_index)).await?;\n// -> active Lighter binding conflicts at client_order_index 42\n\n// after: allocate a new index per submission\nlet idx = next_unique_client_order_index();\nsubmit_order(order.with_client_order_index(idx)).await?;","handlingStrategy":"validation","validationCode":"// before submitting, allocate a guaranteed-unique index\nlet idx = next_unique_client_order_index(); // atomic counter per account","typeGuard":null,"tryCatchPattern":"match submit_order(order).await {\n    Err(e) if e.to_string().contains(\"binding conflicts\") => {\n        log::error!(\"duplicate order identity: {e:#}\");\n        // regenerate cloid + index and resubmit once\n        let order = order.with_new_identity();\n        submit_order(order).await?;\n    }\n    r => r?,\n}","preventionTips":["Never reuse client_order_index/cloid on retries — allocate fresh ones","Use a single atomic counter or coordinator for index assignment","Serialize order submissions across strategy instances","Rebuild bindings from exchange reconciliation after reconnects"],"tags":["orders","duplicate-key","state-management","rust"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}