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
- Keep the exec algorithm registered until after the trader's stop/cleanup completes.
- Verify the algorithm's component_id matches the id used when add_exec_algorithm registered the hooks.
- 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
- Stop the trader before removing exec algorithms so cleanup hooks run with live actors
- Do not manually remove actors the trader manages
- Align algorithm component_id with the id used at registration
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
- Execution algorithm {restore_actor_id} not found while resto
- std::mem::take(&mut self.shutdown_errors).join("; ")
- std::mem::take(&mut self.shutdown_errors).join("; ")
- joined shutdown errors (std::mem::take(&mut self.shutdown_er
- {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/87c18993ecc0f96c.
Report an issue: GitHub.