nautechsystems/nautilus_trader · error · anyhow::Error

Interval exceeds u64 nanoseconds

Error message

Interval exceeds u64 nanoseconds

What it means

duration_to_nanos converts a std Duration into a DurationNanos (u64 nanoseconds). Durations longer than u64::MAX nanoseconds (~584 years) cannot be represented, so TryFrom fails and the function returns 'Interval exceeds u64 nanoseconds'. It is used by set_timer for timer intervals.

Source

Thrown at crates/common/src/clock.rs:710

}

type SetTimeAlertNsHandler<'a> =
    dyn Fn(&str, UnixNanos, Option<TimeEventCallback>, Option<bool>) -> anyhow::Result<()> + 'a;
type NextTimeNsHandler<'a> = dyn Fn(&str) -> Option<UnixNanos> + 'a;
type SetTimerNsHandler<'a> = dyn Fn(
        &str,
        DurationNanos,
        Option<UnixNanos>,
        Option<UnixNanos>,
        Option<TimeEventCallback>,
        Option<bool>,
        Option<bool>,
    ) -> anyhow::Result<()>
    + 'a;

fn duration_to_nanos(duration: Duration) -> anyhow::Result<DurationNanos> {
    DurationNanos::try_from(duration)
        .map_err(|_| anyhow::anyhow!("Interval exceeds u64 nanoseconds"))
}

/// Registry for timer event callbacks.
///
/// Provides shared callback registration and retrieval logic used by both
/// `TestClock` and `LiveClock`.
#[derive(Debug, Default)]
pub struct CallbackRegistry {
    default_callback: Option<TimeEventCallback>,
    callbacks: AHashMap<Ustr, TimeEventCallback>,
}

impl CallbackRegistry {
    /// Creates an empty callback registry.
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Clamp or validate the interval before calling set_timer (e.g. cap at a sane maximum like Duration::from_secs(u64::MAX / 1_000_000_000)).
  2. Fix the interval computation so it cannot overflow — compute in u128 or use checked_mul when converting seconds/millis to nanoseconds.
  3. If 'never fire' semantics are needed, model it explicitly (no timer) rather than passing a gigantic Duration.
  4. Propagate the anyhow error from set_timer and surface a clear configuration message instead of panicking.

Example fix

// before
let interval = Duration::from_secs(years * 365 * 24 * 3600);
clock.set_timer(name, callback, start, interval)?; // may overflow u64 ns

// after
let interval = Duration::from_secs(
    (years as u128 * 365 * 24 * 3600).min(u64::MAX as u128 / 1_000_000_000) as u64,
);
clock.set_timer(name, callback, start, interval)?;
Defensive patterns

Strategy: validation

Validate before calling

# Rust
const MAX_INTERVAL: Duration = Duration::from_secs(u64::MAX / 1_000_000_000);
fn validate_interval(d: Duration) -> anyhow::Result<()> {
    anyhow::ensure!(d <= MAX_INTERVAL, "interval {d:?} exceeds u64 nanoseconds");
    Ok(())
}

Type guard

fn fits_u64_nanos(d: Duration) -> bool {
    d.as_secs() <= u64::MAX / 1_000_000_000
}

Try / catch

match clock.set_timer(name, callback, start, interval) {
    Ok(()) => {},
    Err(e) if e.to_string().contains("exceeds u64 nanoseconds") => {
        tracing::error!("timer interval too large: {e}; clamping");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling set_timer (on TestClock or LiveClock) with a Duration whose nanosecond count exceeds u64::MAX — e.g. programmatically built durations like Duration::from_secs(u64::MAX) or huge multiplier arithmetic that overflows the nanosecond range.

Common situations: Config parsers computing timer intervals with overflow (seconds * 1e9 in a wider type then converting); sentinel 'infinite' timeouts encoded as enormous durations; fuzz/property tests generating extreme Duration values.

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


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