nautechsystems/nautilus_trader · error

Timer '{name}' next event time {} would be in the past (curr

Error message

Timer '{name}' next event time {} would be in the past (current time is {ts_now})

What it means

validate_and_prepare_timer checks that the computed first event time (start_time_ns + interval_ns) is not in the past. When it is and allow-past handling is disabled, the timer would never fire at the requested time, so set_timer_ns rejects the request instead of silently registering a dead timer.

Source

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

    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(),
            );
        }

        if !allow_past && stop_time <= ts_now {
            anyhow::bail!(
                "Timer '{name}' stop time {} is in the past (current time is {ts_now})",
                stop_time.to_rfc3339(),

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set start_time_ns to the current time (clock.timestamp_ns()) or a future instant before calling set_timer_ns.
  2. Enable the allow-past option if immediate firing of missed intervals is acceptable in your setup.
  3. Recompute the schedule relative to 'now' each time the strategy starts rather than persisting absolute start times.
  4. Catch the error and fall back to registering the timer with a now-based start time.

Example fix

// before
clock.set_timer_ns("my_timer", interval_ns, saved_start_time_ns);
// after
let now = clock.timestamp_ns();
let start = if saved_start_time_ns <= now { now } else { saved_start_time_ns };
clock.set_timer_ns("my_timer", interval_ns, start);
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_valid_timer_start(clock: &dyn Clock, start_time_ns: u64, interval_ns: u64) -> anyhow::Result<u64> {
    let next = start_time_ns.checked_add(interval_ns)
        .ok_or_else(|| anyhow::anyhow!("timer first event overflows UnixNanos"))?;
    if next <= clock.timestamp_ns() {
        anyhow::bail!("timer first event {next} would be in the past");
    }
    Ok(start_time_ns)
}

Try / catch

match clock.set_timer_ns(name, interval_ns, start, stop, None, false, false) {
    Ok(()) => {},
    Err(e) if e.to_string().contains("would be in the past") => {
        log::warn!("restarting timer at now: {e}");
        clock.set_timer_ns(name, interval_ns, clock.timestamp_ns(), stop, None, false, false)?;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling set_timer_ns with start_time_ns (plus interval_ns) earlier than clock.timestamp_ns() while allow_past is false — typically on a LiveClock.

Common situations: Reusing a saved timer spec across sessions; backtest-generated start times replayed live; slow startup so the process reaches timer registration after the intended start time; clock skew between machines.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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