nautechsystems/nautilus_trader · error

Actor '{actor_id}' is already tracked by trader

Error message

Actor '{actor_id}' is already tracked by trader

What it means

add_actor_id_for_lifecycle registers just an ActorId (for a native DataActor type) with the trader for lifecycle management. If the ID is already in actor_ids the call bails, since two lifecycle-tracked components cannot share one ID.

Source

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

        Ok(())
    }

    /// Adds an actor ID to the trader's lifecycle management without consuming the actor.
    ///
    /// This is useful when the actor is already registered in the global component registry
    /// but the trader needs to track it for lifecycle management. The caller is responsible
    /// for ensuring the actor is properly registered in the global registries.
    ///
    /// # Errors
    ///
    /// Returns an error if the actor ID is already tracked by this trader.
    pub fn add_actor_id_for_lifecycle<T>(&mut self, actor_id: ActorId) -> anyhow::Result<()>
    where
        T: DataActor + DataActorNative + Debug + 'static,
    {
        // Check for duplicate registration
        if self.actor_ids.contains(&actor_id) {
            anyhow::bail!("Actor '{actor_id}' is already tracked by trader");
        }

        // Store actor ID for lifecycle management
        self.actor_ids.push(actor_id);
        self.actor_state_callbacks.insert(
            actor_id,
            ComponentStateCallbacks {
                load: Self::load_component_state::<T>,
                save: Self::save_component_state::<T>,
            },
        );

        log::debug!(
            "Added actor ID '{actor_id}' to trader {} for lifecycle management",
            self.trader_id
        );

        Ok(())

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Register each actor ID exactly once per trader.
  2. Don't mix add_actor and add_actor_id_for_lifecycle for the same ID.
  3. Create a fresh Trader per run/test instead of reusing a populated one.

Example fix

// before
trader.add_actor_id_for_lifecycle::<MyActor>(actor_id)?;
trader.add_actor_id_for_lifecycle::<MyActor>(actor_id)?; // duplicate
// after
if !trader.actor_ids.contains(&actor_id) {
    trader.add_actor_id_for_lifecycle::<MyActor>(actor_id)?;
}
Defensive patterns

Strategy: validation

Validate before calling

if trader.actor_ids().contains(&actor_id) {
    return Ok(()); // or skip
}
trader.add_actor_id_for_lifecycle::<T>(actor_id)?;

Type guard

fn lifecycle_id_registered(trader: &Trader, id: &ActorId) -> bool {
    trader.actor_ids().contains(id)
}

Try / catch

match trader.add_actor_id_for_lifecycle::<T>(actor_id) {
    Err(e) if e.to_string().contains("already tracked by trader") => Ok(()),
    other => other,
}

Prevention

When it happens

Trigger: Calling the public add_actor_id_for_lifecycle twice with the same ActorId, or after that ID was already registered through add_actor (which also pushes into actor_ids).

Common situations: Double registration during node/kernel setup, registering the same actor both as a full component and as an ID for lifecycle, test code re-invoking setup per case without a fresh trader.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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