nautechsystems/nautilus_trader · error · anyhow::Error
Timer '{name}' first event time exceeds UnixNanos range
Error message
Timer '{name}' first event time exceeds UnixNanos range What it means
NautilusTrader's clock validates that a timer's first fire time fits in the UnixNanos (i64 nanoseconds) domain. When registering a timer whose start_time_ns plus interval_ns would overflow the nanosecond range, the checked addition fails and this error is returned instead of silently wrapping. It protects downstream time arithmetic from overflow-corrected garbage timestamps.
Source
Thrown at crates/common/src/clock.rs:857
fire_immediately: Option<bool>,
ts_now: UnixNanos,
) -> anyhow::Result<(Ustr, UnixNanos, Option<UnixNanos>, bool, bool)> {
check_valid_string_utf8(name, stringify!(name))?;
check_positive_u64(interval_ns.as_u64(), stringify!(interval_ns))?;
let name = Ustr::from(name);
let allow_past = allow_past.unwrap_or(true);
let fire_immediately = fire_immediately.unwrap_or(false);
let start_time_ns = start_time_ns
.filter(|start_time_ns| *start_time_ns != 0)
.unwrap_or(ts_now);
let next_event_time = if fire_immediately {
start_time_ns
} else {
start_time_ns.checked_add(interval_ns).ok_or_else(|| {
anyhow::anyhow!("Timer '{name}' first event time exceeds UnixNanos range")
})?
};
if !allow_past && next_event_time < ts_now {
anyhow::bail!(
"Timer '{name}' next event time {} would be in the past (current time is {ts_now})",
next_event_time.to_rfc3339(),
);
}
if let Some(stop_time) = stop_time_ns {
if stop_time <= start_time_ns {
anyhow::bail!(
"Timer '{name}' stop time {} must be after start time {}",
stop_time.to_rfc3339(),
start_time_ns.to_rfc3339(),
);
}View on GitHub (pinned to 18893faf8b)
Solutions
- Reduce the timer interval so start_time_ns + interval_ns stays below ~9.2e18 nanoseconds
- Verify units: interval_ns must already be in nanoseconds; convert seconds/ms with Duration::from_secs(...).as_nanos() as u64
- If the intent is a far-future/never-firing timer, use a smaller sentinel interval or cancel the timer explicitly instead
- Check that start_time_ns itself is a plausible current-time UnixNanos value and not already near the limit
Example fix
// before
clock.set_timer_ns("poll", u64::MAX, None, None, None, false, false, None)?;
// after
let one_hour_ns = std::time::Duration::from_secs(3600).as_nanos() as u64;
clock.set_timer_ns("poll", one_hour_ns, None, None, None, false, false, None)?; Defensive patterns
Strategy: validation
Validate before calling
let max_interval: u64 = (i64::MAX as u128 - start_time_ns as u128) as u64; assert!(interval_ns <= max_interval, "timer interval overflows UnixNanos");
Try / catch
match clock.set_timer_ns(name, interval, None, None, None, false, false, None) {
Ok(()) => {}
Err(e) if e.to_string().contains("exceeds UnixNanos range") => {
log::error!("interval too large for nanosecond domain: {e}");
}
Err(e) => return Err(e),
} Prevention
- Always convert durations to nanoseconds via Duration::as_nanos() rather than hand-written constants
- Reject intervals beyond a sane maximum (e.g. > 100 years) at config load time
- Never use u64::MAX-style sentinels as 'infinite' intervals
When it happens
Trigger: Calling set_time_alert_ns/set_timer_ns with an interval_ns so large that start_time_ns + interval_ns exceeds i64::MAX nanoseconds (~year 2262); e.g. passing an interval expressed in milliseconds or seconds by mistake, or an absurdly long duration near u64/i64 nanosecond limits.
Common situations: Unit-conversion mistakes (passing nanoseconds constant like 86_400_000_000_000 multiplied again, or using Duration::MAX-style values), scheduling timers intended to 'never fire' by using a huge interval, misconfigured timeout config parsed into the wrong unit.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- WETH balance overflow for included transaction {tx_hash} at
- Event '{}' should have associated handler
- timer event sender was unset for Rust callback system
- DurationNanos overflow in from_micros
- {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/423b6c7406726044.
Report an issue: GitHub.