nautechsystems/nautilus_trader · error

Execution algorithm {cleanup_actor_id} not found while clean

Error message

Execution algorithm {cleanup_actor_id} not found while cleaning subscriptions

What it means

The cleanup closure registered by add_exec_algorithm unsubscribes all strategy events for the algorithm, but first resolves the algorithm via try_get_actor_unchecked by actor id. If the actor cannot be found at cleanup time it fails with this message. This keeps subscription teardown symmetric with registration.

Source

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

                    .filter(|order| {
                        !order.is_closed() && order.exec_algorithm_id() == Some(exec_algorithm_id)
                    })
                    .map(|order| order.strategy_id())
                    .collect::<Vec<_>>()
            };
            strategy_ids.sort_unstable();
            strategy_ids.dedup();

            for strategy_id in strategy_ids {
                algo.subscribe_to_strategy_events(strategy_id);
            }

            Ok(())
        });
        let cleanup_actor_id = actor_id;
        let cleanup_fn: ExecutionAlgorithmSubscriptionFn = Box::new(move || {
            let Some(mut algo) = try_get_actor_unchecked::<T>(&cleanup_actor_id) else {
                anyhow::bail!(
                    "Execution algorithm {cleanup_actor_id} not found while cleaning subscriptions"
                );
            };
            algo.unsubscribe_all_strategy_events();
            Ok(())
        });
        let endpoint: Ustr = format!("{exec_algorithm_id}.execute").into();
        let handler = ShareableMessageHandler::from_typed(move |command: &TradingCommand| {
            if let Some(mut algo) = try_get_actor_unchecked::<T>(&actor_id) {
                if let Err(e) = algo.execute(command.clone()) {
                    log::error!("Error executing command on algorithm {actor_id}: {e}");
                }
            } else {
                log::error!("Execution algorithm {actor_id} not found in registry");
            }
        });
        msgbus::register_any(endpoint.into(), handler);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Keep the exec algorithm registered until after the trader's stop/cleanup completes.
  2. Verify the algorithm's component_id matches the id used when add_exec_algorithm registered the hooks.
  3. If the algorithm is intentionally gone, remove/re-register it via add_exec_algorithm so hooks reference a live actor.

Example fix

// before
trader.remove_actor(&algo_id); // removed too early
trader.stop()?; // cleanup_fn can't find actor

// after
trader.stop()?; // cleanup runs while actor is live
trader.remove_actor(&algo_id);
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = trader.stop() {
    if e.to_string().contains("not found while cleaning subscriptions") {
        log::warn("exec algorithm already removed; cleanup skipped");
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: cleanup_fn executes (e.g. after a start failure rolling back restored subscriptions, or on stop) while try_get_actor_unchecked::<T>(&cleanup_actor_id) returns None because the algorithm was disposed, removed, or never registered under that id.

Common situations: A start failure path cleaning up restored subscriptions after the algorithm itself was already disposed; mismatched component_id vs registered actor id; manual actor removal from the registry while the trader still holds the cleanup hook.

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/87c18993ecc0f96c. Report an issue: GitHub.