nautechsystems/nautilus_trader · error · anyhow::Error

DataActor {actor_id} already registered with trader {existin

Error message

DataActor {actor_id} already registered with trader {existing_trader_id}

What it means

`DataActor::register` refuses to run twice. If the actor already holds a `trader_id`, calling registration again would silently re-bind the actor to another trader; the library bails instead, preserving a one-to-one actor/trader relationship for the actor's lifetime.

Source

Thrown at crates/common/src/actor/data_actor.rs:4129

            )
        });
        CacheApi::new(cache.as_ref())
    }

    /// Register the data actor with a trader.
    ///
    /// # Errors
    ///
    /// Returns an error if the actor has already been registered with a trader
    /// or if the provided dependencies are invalid.
    pub fn register(
        &mut self,
        trader_id: TraderId,
        clock: Rc<RefCell<dyn Clock>>,
        cache: Rc<RefCell<Cache>>,
    ) -> anyhow::Result<()> {
        if let Some(existing_trader_id) = self.trader_id {
            anyhow::bail!(
                "DataActor {} already registered with trader {existing_trader_id}",
                self.actor_id
            );
        }

        // Validate clock by attempting to access it
        {
            let _timestamp = clock.borrow().timestamp_ns();
        }

        // Validate cache by attempting to access it
        {
            let _cache_borrow = cache.borrow();
        }

        self.trader_id = Some(trader_id);
        self.clock = Some(clock);
        self.cache = Some(cache);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Create a new DataActor instance for the new registration
  2. Ensure each actor is registered exactly once per trader lifecycle
  3. If re-registering after a partial failure is intended, reset/rebuild the actor instead of reusing it

Example fix

// before
actor.register(trader_id_a.clone(), ...);
actor.register(trader_id_b.clone(), ...); // bails
// after
let actor_a = MyActor::new();
let actor_b = MyActor::new();
actor_a.register(trader_id_a, ...);
actor_b.register(trader_id_b, ...);
Defensive patterns

Strategy: validation

Validate before calling

if actor_is_registered(&actor) {
    return Err(anyhow::anyhow!("actor already registered; create a new instance"));
}

Type guard

fn is_unregistered(actor: &DataActor) -> bool {
    actor.trader_id().is_none()
}

Try / catch

if let Err(e) = actor.register(trader_id, clock.clone(), cache.clone()) {
    log::warn!("registration skipped: {e}"); // treat double-registration as idempotent no-op if safe
}

Prevention

When it happens

Trigger: Calling `register(trader_id, clock, cache)` on a DataActor that was previously registered; reusing one actor instance across two traders or a trader restart without constructing a new actor.

Common situations: Building a second strategy/trader in the same process and accidentally sharing an actor; retrying registration after a failure; running a backtest twice with cached actor instances.

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