{"record":{"id":"58917d4c8dcd7b25","repo":"nautechsystems/nautilus_trader","slug":"external-order-claim-for-instrument-id-appears-m","errorCode":null,"errorMessage":"External order claim for {instrument_id} appears more than once for {strategy_id}","messagePattern":"External order claim for (.+?) appears more than once for (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/common/src/cache/mod.rs","lineNumber":2373,"sourceCode":"    /// External orders, fills, and materialized reconciliation activity for matching instrument\n    /// IDs are assigned to the strategy. Existing claims owned by other strategies are preserved.\n    ///\n    /// The operation is atomic: either every requested instrument is claimed or the cache is\n    /// unchanged. Passing an empty slice clears all claims owned by the strategy.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if an instrument is repeated or claimed by another strategy.\n    pub fn set_external_order_claims(\n        &mut self,\n        strategy_id: StrategyId,\n        instrument_ids: &[InstrumentId],\n    ) -> anyhow::Result<()> {\n        let mut requested = AHashSet::with_capacity(instrument_ids.len());\n\n        for instrument_id in instrument_ids {\n            if !requested.insert(*instrument_id) {\n                anyhow::bail!(\n                    \"External order claim for {instrument_id} appears more than once for {strategy_id}\"\n                );\n            }\n\n            if let Some(existing) = self.external_order_claims.get(instrument_id)\n                && *existing != strategy_id\n            {\n                anyhow::bail!(\n                    \"External order claim for {instrument_id} already exists for {existing}\"\n                );\n            }\n        }\n\n        self.external_order_claims\n            .retain(|_, owner| *owner != strategy_id);\n        self.external_order_claims.extend(\n            requested\n                .into_iter()","sourceCodeStart":2355,"sourceCodeEnd":2391,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/cache/mod.rs#L2355-L2391","documentation":"`claim_external_orders` iterates the requested instrument IDs and uses an AHashSet to detect duplicates within a single call. The same instrument appearing twice in one request is ambiguous (which strategy claim wins?) so it bails before mutating any state.","triggerScenarios":"Calling `claim_external_orders(strategy_id, &instruments)` with a slice containing the same `InstrumentId` twice in the same invocation.","commonSituations":"Building the instrument list by concatenating config lists that overlap; a loop that appends the same instrument each iteration; deduplication omitted when merging per-strategy and global instrument sets.","solutions":["Deduplicate the instrument list before calling claim_external_orders (e.g. collect into a HashSet/AHashSet first)","Fix the list construction so each instrument appears once per call","Split into multiple calls if repeated claims across time are intended (per-call uniqueness still required)"],"exampleFix":"// before\nlet ids = vec![btc_usd, eth_usd, btc_usd];\ncache.claim_external_orders(strategy_id, &ids)?;\n// after\nlet ids: Vec<InstrumentId> = [btc_usd, eth_usd, btc_usd].into_iter().collect::<AHashSet<_>>().into_iter().collect();\ncache.claim_external_orders(strategy_id, &ids)?;","handlingStrategy":"validation","validationCode":"let unique: AHashSet<InstrumentId> = instruments.iter().copied().collect();\nif unique.len() != instruments.len() {\n    return Err(anyhow::anyhow!(\"duplicate instruments in claim request\"));\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = cache.claim_external_orders(strategy_id, &instruments) {\n    if e.to_string().contains(\"appears more than once\") {\n        let deduped: Vec<_> = instruments.iter().copied().collect::<AHashSet<_>>().into_iter().collect();\n        cache.claim_external_orders(strategy_id, &deduped)?;\n    } else { return Err(e); }\n}","preventionTips":["Deduplicate instrument lists before claiming","Avoid concatenating overlapping config lists","Write a startup check that instruments appear once per claim call"],"tags":["cache","duplicate","orders"],"backgroundTag":"duplicate-entry","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}