nautechsystems/nautilus_trader · error

Timer '{name}' stop time {} is in the past (current time is

Error message

Timer '{name}' stop time {} is in the past (current time is {ts_now})

What it means

When a stop_time_ns is supplied, set_timer_ns also requires it to be in the future (unless allow_past is set). A stop time already in the past means the timer would be born already expired, so the request is rejected rather than registering a useless timer.

Source

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

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

    Ok((
        name,
        start_time_ns,
        stop_time_ns,
        allow_past,
        fire_immediately,
    ))
}

/// A deterministic clock for controlled time advancement.
///
/// The clock stores a manual timestamp, schedules [`TestTimer`] instances, and returns due events

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Recompute stop_time_ns relative to the current clock time before registering the timer.
  2. Skip timer registration entirely if the scheduled stop time has already passed (the timer's work window is over).
  3. Enable allow_past if immediate expiry / past semantics are acceptable.
  4. Guard in caller code: if stop_time_ns <= clock.timestamp_ns() { return; } before calling set_timer_ns.

Example fix

// before
clock.set_timer_ns("session_timer", interval_ns, start, Some(saved_stop_ns), None, false, false);
// after
let now = clock.timestamp_ns();
if saved_stop_ns > now {
    clock.set_timer_ns("session_timer", interval_ns, start, Some(saved_stop_ns), None, false, false);
}
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_future_stop(clock: &dyn Clock, stop_time_ns: Option<u64>) -> anyhow::Result<()> {
    if let Some(stop) = stop_time_ns {
        anyhow::ensure!(stop > clock.timestamp_ns(), "stop {stop} already in the past");
    }
    Ok(())
}

Try / catch

match clock.set_timer_ns(name, interval_ns, start, stop, None, false, false) {
    Err(e) if e.to_string().contains("stop time") && e.to_string().contains("in the past") => {
        log::warn!("session window already closed, skipping timer");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling set_timer_ns with Some(stop_time_ns) <= clock.timestamp_ns() while allow_past is false.

Common situations: Session end times persisted from a previous run and reused; long-running deployment crossing the configured daily/weekly stop time; clock skew; seconds-vs-nanoseconds conversion mistakes making the value tiny and thus 'past'.

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