nautechsystems/nautilus_trader · error

Cannot remove strategy, {strategy_id} not found

Error message

Cannot remove strategy, {strategy_id} not found

What it means

remove_strategy is a guard-clause in Trader::remove_strategy: it checks strategy_ids membership and bails if the StrategyId was never registered (or was already removed). The trader refuses to remove an unknown strategy rather than silently succeeding.

Source

Thrown at crates/system/src/trader.rs:1424

        };

        Ok(handler)
    }

    /// Removes the strategy with the given `strategy_id`.
    ///
    /// Will stop the strategy first if it is currently running. Disposes the strategy
    /// and removes it from the trader's tracking along with its event subscriptions.
    ///
    /// # Errors
    ///
    /// Returns an error if the strategy is not registered, the cache is already borrowed, or
    /// disposal fails. A cache borrow failure preserves the strategy registration and its external
    /// order claims. A failed disposal keeps the strategy registered and tracked, and leaves it
    /// `Faulted`; see [`Component::dispose`]. Calling this again retires the strategy.
    pub fn remove_strategy(&mut self, strategy_id: &StrategyId) -> anyhow::Result<()> {
        if !self.strategy_ids.contains(strategy_id) {
            anyhow::bail!("Cannot remove strategy, {strategy_id} not found");
        }

        // Stop if running, then dispose
        let _ = stop_component(&strategy_id.inner());
        self.retire_strategy(*strategy_id)?;

        log::info!(
            "Removed strategy {strategy_id} from trader {}",
            self.trader_id
        );
        Ok(())
    }

    /// Disposes an actor, then releases everything its registration created.
    ///
    /// Each component is retired completely before the next one starts, so a failure part way
    /// through a bulk operation leaves the trader's bookkeeping consistent with the registries.
    fn retire_actor(&mut self, actor_id: ActorId) -> anyhow::Result<()> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check trader.strategy_ids().contains(strategy_id) before calling remove_strategy
  2. Ensure the strategy was registered via trader.add_strategy before removal
  3. Track removal state so teardown logic doesn't call remove_strategy twice
  4. Log the registered strategy IDs when the removal fails to spot ID mismatches

Example fix

// before
trader.remove_strategy(&strategy_id)?;
// after
if trader.strategy_ids().contains(&strategy_id) {
    trader.remove_strategy(&strategy_id)?;
}
Defensive patterns

Strategy: validation

Validate before calling

if trader.strategy_ids().contains(&strategy_id) { trader.remove_strategy(&strategy_id)?; }

Try / catch

match trader.remove_strategy(&id) { Err(e) if e.to_string().contains("not found") => {/* already removed */}, other => other?, }

Prevention

When it happens

Trigger: Calling trader.remove_strategy(&strategy_id) with an ID that was never added, a strategy already removed in a prior call, or an ID from a different trader/config.

Common situations: Double teardown during shutdown; config lists a strategy that failed to register at startup; ID constructed manually instead of taken from the trader's registry.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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