nautechsystems/nautilus_trader · error · anyhow::Error

External order claim for {instrument_id} already exists for

Error message

External order claim for {instrument_id} already exists for {existing}

What it means

External order claims map each `InstrumentId` to exactly one strategy. When claiming, if the instrument is already claimed by a different strategy, the call bails so two strategies cannot both own external order claims for the same instrument.

Source

Thrown at crates/common/src/cache/mod.rs:2381

    /// 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()
                .map(|instrument_id| (instrument_id, strategy_id)),
        );

        Ok(())
    }

    /// Adds external order claims for `strategy_id` without replacing its existing claims.
    ///

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Remove the overlapping instrument from one strategy's external-claim list
  2. Use the same strategy for all external orders on that instrument
  3. Clear the existing claim (via the appropriate unclaim/release path or fresh cache) before re-claiming with a different strategy
  4. Audit strategy configs at startup to detect instrument overlaps early

Example fix

// before
strategy_a.claim_external_orders(&[btc_usd])?;
strategy_b.claim_external_orders(&[btc_usd])?; // bails
// after
strategy_a.claim_external_orders(&[btc_usd])?; // single owner
// remove btc_usd from strategy_b's claim list
Defensive patterns

Strategy: validation

Validate before calling

for id in instruments {
    if let Some(existing) = existing_claims.get(id) && *existing != strategy_id {
        return Err(anyhow::anyhow!("{id} already claimed by {existing}"));
    }
}

Try / catch

if let Err(e) = cache.claim_external_orders(strategy_id, &instruments) {
    if e.to_string().contains("already exists for") {
        log::error!("instrument claim conflict: {e}; review strategy configs");
        return Err(e);
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: Calling `claim_external_orders` with an instrument that `external_order_claims` already maps to a different strategy_id; two strategies configuring claims over the same instrument.

Common situations: Strategy configuration overlap where both strategies list the same symbol for external order claiming; reconfiguring strategies without clearing prior claims; loading the same config twice under different strategy IDs.

Related errors


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