nautechsystems/nautilus_trader · error
Callback should exist
Error message
Callback should exist
What it means
This panic comes from `.expect("Callback should exist")` in `LiveClock::set_time_alert_ns` in crates/common/src/live/clock.rs. When no new callback is supplied, the method assumes a callback was previously registered under `name` and tries to fetch it from the callback registry; if the registry has no entry for that name, the invariant is broken and the clock panics.
Source
Thrown at crates/common/src/live/clock.rs:168
) -> anyhow::Result<()> {
let ts_now = self.get_time_ns();
let (name, alert_time_ns) =
validate_and_prepare_time_alert(name, alert_time_ns, allow_past, ts_now)?;
check_predicate_true(
callback.is_some() | self.callbacks.has_any_callback(&name),
"No callbacks provided",
)?;
self.replace_existing_timer_if_needed(&name);
let callback = if let Some(callback) = callback {
self.callbacks.register_callback(name, callback.clone());
callback
} else {
self.callbacks
.get_callback(&name)
.expect("Callback should exist")
};
// Safe to calculate interval now that we've ensured alert_time_ns >= ts_now
let interval_ns = create_valid_interval(alert_time_ns - ts_now);
let fire_immediately = alert_time_ns == ts_now;
let sender = self.resolve_time_event_sender();
let mut timer = LiveTimer::new(
name,
interval_ns,
ts_now,
Some(alert_time_ns),
callback,
fire_immediately,
sender,
);
timer.start();View on GitHub (pinned to 18893faf8b)
Solutions
- Always pass a callback (Some(...)) the first time you register a given time-alert name
- Ensure the name matches exactly a previously registered callback; verify with `callback_registered(&name)` before passing None
- If the timer may have expired and cleaned up its callback, re-register with a fresh callback instead of None
- Check that no code path cancels the timer/callback between registration and re-use
Example fix
// before
clock.set_time_alert_ns("my_alert", alert_time_ns, None); // panics if callback was cleaned up
// after
if !clock.callback_registered(&"my_alert".into()) {
clock.set_time_alert_ns("my_alert", alert_time_ns, Some(Callback::from(|event| { /* handle */ })));
} else {
clock.set_time_alert_ns("my_alert", alert_time_ns, None);
} Defensive patterns
Strategy: type-guard
Validate before calling
if !clock.callback_registered(&name) {
// register with Some(callback) instead of None
} Type guard
fn can_rearm(clock: &LiveClock, name: &str) -> bool { clock.callback_registered(&name.into()) } Prevention
- Pass Some(callback) on first registration of a name
- Check callback_registered before re-arming with None
- Use constant timer names; avoid cancel_timer for names you will re-arm with None
When it happens
Trigger: Calling `live_clock.set_time_alert_ns(name, alert_time_ns, None)` (or the `_` variant) for a `name` that was never registered with a callback — e.g. the alert was set with None before any registration, the callback was removed via `cancel_timer`/registry clearing, or the name is mistyped.
Common situations: Re-registering a persistent time alert after the timer expired and was cleaned up (callback registry entry dropped), typos in timer names between calls, or actors re-arming alerts in `on_stop`/`on_start` cycles where the previous callback was cancelled.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- timer event sender was unset for Rust callback system
- registered callback lease was not acquired
- Failed to set spread quote timer
- Failed to set spread quote vega pricing timeout
- Event '{}' should have associated handler
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/380138f5fce9e87d.
Report an issue: GitHub.