nautechsystems/nautilus_trader · error

Failed to restore strategy {strategy_id} state: {e:#}

Error message

Failed to restore strategy {strategy_id} state: {e:#}

What it means

Raised in `Trader::load_state` when a strategy's registered `on_load` state-restoration callback returns an error. The persisted state was read from the cache successfully, but the strategy could not restore it, so recovery aborts with this message.

Source

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

            };

            (callbacks.load)(actor_id.inner(), state)
                .map_err(|e| anyhow::anyhow!("Failed to restore actor {actor_id} state: {e:#}"))?;
        }

        for (strategy_id, callbacks) in strategy_callbacks {
            let state = cache
                .borrow()
                .load_strategy_state(&strategy_id)
                .map_err(|e| {
                    anyhow::anyhow!("Failed to load strategy {strategy_id} state: {e:#}")
                })?;
            let Some(state) = state.filter(|state| !state.is_empty()) else {
                continue;
            };

            (callbacks.load)(strategy_id.inner(), state).map_err(|e| {
                anyhow::anyhow!("Failed to restore strategy {strategy_id} state: {e:#}")
            })?;
        }

        Ok(())
    }

    /// Saves actor and strategy state in registration order.
    ///
    /// Empty state is persisted, while a cache without database backing does not invoke
    /// component callbacks. All callbacks and updates receive an attempt before errors return.
    ///
    /// # Errors
    ///
    /// Returns an error containing every component callback or persistence failure.
    pub(crate) fn save_state(trader: &Rc<RefCell<Self>>) -> anyhow::Result<()> {
        let (cache, actor_callbacks, strategy_callbacks) = {
            let trader = trader.borrow();
            let actor_callbacks = trader.actor_state_callbacks()?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the wrapped `{e:#}` for the exact restore failure and align the load callback with the persisted schema
  2. Ensure the strategy implementation/version that saved the state matches the one restoring it; add migration logic to the load callback if needed
  3. Delete the strategy's stale persisted key so it starts with empty state, then let it re-save
  4. Keep library versions consistent between state-saving and state-restoring runs
Defensive patterns

Strategy: try-catch

Validate before calling

// round-trip check the strategy state before relying on recovery
let state = cache.load_strategy_state(&strategy_id)?;
strategy.test_restore(state.clone())?;

Try / catch

do {
    system.start_with_recovery()
} catch e if e.contains("Failed to restore strategy") => {
    // migrate the schema or delete the stale key, then restart
}

Prevention

When it happens

Trigger: Trader start with state recovery enabled; the cache returns non-empty state for the strategy and the strategy's `load` callback errors — typically because the saved payload does not match the strategy's current state schema (version upgrade, changed fields, wrong strategy type).

Common situations: Restarting after a nautilus upgrade with state saved by the older version; refactoring a strategy's saved-state struct without a migration; a cache shared between configurations containing state for a different strategy instance.

Related errors


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