nautechsystems/nautilus_trader · error · anyhow::Error
External order claim for {instrument_id} appears more than o
Error message
External order claim for {instrument_id} appears more than once for {strategy_id} What it means
`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.
Source
Thrown at crates/common/src/cache/mod.rs:2373
/// External orders, fills, and materialized reconciliation activity for matching instrument
/// IDs are assigned to the strategy. Existing claims owned by other strategies are preserved.
///
/// The operation is atomic: either every requested instrument is claimed or the cache is
/// unchanged. Passing an empty slice clears all claims owned by the strategy.
///
/// # Errors
///
/// Returns an error if an instrument is repeated or claimed by another strategy.
pub fn set_external_order_claims(
&mut self,
strategy_id: StrategyId,
instrument_ids: &[InstrumentId],
) -> anyhow::Result<()> {
let mut requested = AHashSet::with_capacity(instrument_ids.len());
for instrument_id in instrument_ids {
if !requested.insert(*instrument_id) {
anyhow::bail!(
"External order claim for {instrument_id} appears more than once for {strategy_id}"
);
}
if let Some(existing) = self.external_order_claims.get(instrument_id)
&& *existing != strategy_id
{
anyhow::bail!(
"External order claim for {instrument_id} already exists for {existing}"
);
}
}
self.external_order_claims
.retain(|_, owner| *owner != strategy_id);
self.external_order_claims.extend(
requested
.into_iter()View on GitHub (pinned to 18893faf8b)
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)
Example fix
// before let ids = vec![btc_usd, eth_usd, btc_usd]; cache.claim_external_orders(strategy_id, &ids)?; // after let ids: Vec<InstrumentId> = [btc_usd, eth_usd, btc_usd].into_iter().collect::<AHashSet<_>>().into_iter().collect(); cache.claim_external_orders(strategy_id, &ids)?;
Defensive patterns
Strategy: validation
Validate before calling
let unique: AHashSet<InstrumentId> = instruments.iter().copied().collect();
if unique.len() != instruments.len() {
return Err(anyhow::anyhow!("duplicate instruments in claim request"));
} Try / catch
if let Err(e) = cache.claim_external_orders(strategy_id, &instruments) {
if e.to_string().contains("appears more than once") {
let deduped: Vec<_> = instruments.iter().copied().collect::<AHashSet<_>>().into_iter().collect();
cache.claim_external_orders(strategy_id, &deduped)?;
} else { return Err(e); }
} Prevention
- Deduplicate instrument lists before claiming
- Avoid concatenating overlapping config lists
- Write a startup check that instruments appear once per claim call
When it happens
Trigger: Calling `claim_external_orders(strategy_id, &instruments)` with a slice containing the same `InstrumentId` twice in the same invocation.
Common situations: 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.
Related errors
- `synthetic` {synthetic.id} already exists
- External order claim for {instrument_id} already exists for
- OrderList denied: duplicate {}
- Order in list denied: duplicate {}
- Instrument {instrument_id} not found in cache
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/58917d4c8dcd7b25.
Report an issue: GitHub.