nautechsystems/nautilus_trader · error

DataActor {} must be registered before calling `clock_ref()`

Error message

DataActor {} must be registered before calling `clock_ref()` - trader_id: {:?}

What it means

`DataActor::clock_ref()` panics when the actor's clock handle is `None`, meaning the actor was never registered with a trader (registration injects the clock). Borrowing the clock before registration breaks the actor lifecycle contract.

Source

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

    pub fn timestamp_ns(&self) -> UnixNanos {
        self.clock_ref().timestamp_ns()
    }

    pub(super) fn clock_api(&self) -> ClockApi<'_> {
        let clock = self.clock.as_ref().unwrap_or_else(|| {
            panic!(
                "DataActor {} must be registered before calling `clock()` - trader_id: {:?}",
                self.actor_id, self.trader_id
            )
        });
        ClockApi::new(clock.as_ref())
    }

    fn clock_ref(&self) -> Ref<'_, dyn Clock> {
        self.clock
            .as_ref()
            .unwrap_or_else(|| {
                panic!(
                    "DataActor {} must be registered before calling `clock_ref()` - trader_id: {:?}",
                    self.actor_id, self.trader_id
                )
            })
            .borrow()
    }

    fn cache_api(&self) -> CacheApi<'_> {
        let cache = self.cache.as_ref().unwrap_or_else(|| {
            panic!(
                "DataActor {} must be registered before calling `cache()` - trader_id: {:?}",
                self.actor_id, self.trader_id
            )
        });
        CacheApi::new(cache.as_ref())
    }

    /// Register the data actor with a trader.

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Register the actor with a trader before any clock access.
  2. Move clock access to post-registration lifecycle hooks (`on_start`, handlers).
  3. In tests, run actors through the registered test harness.

Example fix

// before
let actor = MyActor::new();
let now = actor.clock_ref().timestamp_ns(); // panic
// after
trader.add_actor(MyActor::new());
// access clock inside on_start via self.clock_ref()
Defensive patterns

Strategy: type-guard

Validate before calling

assert!(actor.is_registered(), "clock_ref() requires trader registration");

Type guard

fn clock_available(actor: &DataActor) -> bool { actor.is_registered() }

Prevention

When it happens

Trigger: Any read access to the clock via `clock_ref()` on an unregistered `DataActor` (e.g. inside `timestamp_ns()` or custom code calling `clock_ref()` directly).

Common situations: Calling clock-dependent logic during `on_init`/construction; actors used standalone in unit tests without a trader; forgetting `trader.add_actor(...)` before a manual run.

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