nautechsystems/nautilus_trader · critical

Failed to start actor {actor_id}: {start_err}; rollback fail

Error message

Failed to start actor {actor_id}: {start_err}; rollback failed: {rollback_err}

What it means

When an actor fails to start, the TradingSystemController attempts to roll back by removing the half-started actor via remove_actor. If the rollback itself also fails, the original start error would be lost, so both errors are combined into this single aggregated error naming the actor, the original start failure, and the rollback failure.

Source

Thrown at crates/system/src/controller.rs:343

        if !start {
            return Ok(());
        }

        if let Err(start_err) = self.start_strategy(&strategy_id) {
            return Err(self.rollback_strategy_start_failure(strategy_id, start_err));
        }

        Ok(())
    }

    fn rollback_actor_start_failure(
        &self,
        actor_id: ActorId,
        start_err: anyhow::Error,
    ) -> anyhow::Error {
        match self.remove_actor(&actor_id) {
            Ok(()) => start_err,
            Err(rollback_err) => anyhow::anyhow!(
                "Failed to start actor {actor_id}: {start_err}; rollback failed: {rollback_err}"
            ),
        }
    }

    fn rollback_strategy_start_failure(
        &self,
        strategy_id: StrategyId,
        start_err: anyhow::Error,
    ) -> anyhow::Error {
        match self.remove_strategy(&strategy_id) {
            Ok(()) => start_err,
            Err(rollback_err) => anyhow::anyhow!(
                "Failed to start strategy {strategy_id}: {start_err}; rollback failed: {rollback_err}"
            ),
        }
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Fix the root start error ({start_err} in the message) first — the rollback failure is secondary
  2. Investigate why remove_actor failed: check whether the actor was already removed or the registry is locked/inconsistent
  3. Add guards so actors are not started twice concurrently for the same ActorId
  4. Reproduce with logging enabled at DEBUG for the controller to see both failure points in order
Defensive patterns

Strategy: try-catch

Validate before calling

// before start_created_actor
assert!(!controller.has_actor(&actor_id), "actor {actor_id} already registered — would make rollback ambiguous");

Try / catch

match system.start() {
    Err(e) if e.to_string().contains("rollback failed") => {
        log::error!("actor start AND rollback failed — system state inconsistent, restart required: {e:#}");
        // treat the system as unrecoverable; rebuild controller state
    }
    other => other?,
}

Prevention

When it happens

Trigger: start_created_actor fails to start an actor (start_err), and the subsequent remove_actor cleanup also errors (rollback_err) — e.g. the actor is in a state where it cannot be removed, or the component registry is inconsistent.

Common situations: Actor start failure due to bad configuration compounded by a panicked or dead actor that cannot be removed; double-start races where the actor was already removed by another path; kernel/component state corruption after an earlier failure.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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