nautechsystems/nautilus_trader · error
Timer '{name}' stop time {} must be after start time {}
Error message
Timer '{name}' stop time {} must be after start time {} What it means
set_timer_ns supports an optional stop_time_ns that terminates the timer. Validation requires the stop time to be strictly after the start time; if stop_time <= start_time_ns the timer would stop before or exactly when it begins, which is contradictory, so the call fails.
Source
Thrown at crates/common/src/clock.rs:870
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(),
);
}
}
Ok((
name,
start_time_ns,
stop_time_ns,View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure stop_time_ns > start_time_ns, e.g. stop = start + planned_duration_ns, before calling set_timer_ns.
- Check unit conversion: Nautilus times are Unix nanoseconds; multiply seconds by 1_000_000_000.
- Guard the computation: bail or warn in your code if stop_time_ns <= start_time_ns and drop the stop_time (pass None) for an unbounded timer.
- Correct the strategy/session config so the stop deadline postdates the start.
Example fix
// before
let stop = start_time_ns; // wrong: equals start
clock.set_timer_ns("t", interval_ns, start_time_ns, Some(stop), None, false, false);
// after
let stop = start_time_ns + duration_ns; // must be > start
clock.set_timer_ns("t", interval_ns, start_time_ns, Some(stop), None, false, false); Defensive patterns
Strategy: validation
Validate before calling
fn ensure_stop_after_start(start_time_ns: u64, stop_time_ns: Option<u64>) -> anyhow::Result<()> {
if let Some(stop) = stop_time_ns {
anyhow::ensure!(stop > start_time_ns, "stop {stop} must be after start {start_time_ns}");
}
Ok(())
} Try / catch
match clock.set_timer_ns(name, interval_ns, start, stop, None, false, false) {
Err(e) if e.to_string().contains("must be after start time") => {
log::error!("bad timer config: {e}");
// fix config or pass None for unbounded timer
}
other => other?,
} Prevention
- Compute stop = start + duration_ns in nanoseconds; double-check unit conversions
- Unit-test timer schedules with their start/stop relationships
- Validate strategy config stop deadlines exceed start times before registration
When it happens
Trigger: Calling set_timer_ns with Some(stop_time_ns) that is less than or equal to start_time_ns.
Common situations: Off-by-one or unit mix-ups (seconds vs nanoseconds when computing stop = start + duration); copying a stop time from a different schedule; config where stop time was authored for a different start time.
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
- Timer '{name}' alert time {} was in the past (current time i
- Timer '{name}' next event time {} would be in the past (curr
- Timer '{name}' stop time {} is in the past (current time is
- Event '{}' should have associated handler
- fill report start time must not exceed end time
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/24fa46e8b9936128.
Report an issue: GitHub.