influxdata/influxdb · critical
can't be out of range
Error message
can't be out of range
What it means
This is a panic, not a returned error: `new_every` computes the next trigger time by rounding `now` up to the next multiple of the duration, then calls `DateTime::from_timestamp_millis(...).expect("can't be out of range")`. The expect fires only if the computed millisecond timestamp falls outside chrono's representable range — practically only via overflow with extreme durations or a broken time provider.
Solutions
- Verify the duration passed is at most 1 year (the try_new guard) — avoid constructing `new_every` directly with unvalidated durations.
- Fix or replace the TimeProvider mock so `now()` returns a realistic timestamp.
- If you control the code, replace `.expect` with a fallible conversion that returns an error instead of panicking.
- Correct the host system clock if it is wildly wrong.
Example fix
// before
let next_trigger_time = DateTime::from_timestamp_millis(next_trigger_millis).expect("can't be out of range");
// after
let next_trigger_time = DateTime::from_timestamp_millis(next_trigger_millis)
.context("computed next trigger timestamp out of range")?; Defensive patterns
Strategy: validation
Validate before calling
// Guard inputs before constructing the schedule
assert!(duration > Duration::zero() && duration <= parse_duration("1 year").unwrap());
let now_millis = time_provider.now().date_time().timestamp_millis();
assert!(now_millis > 0 && now_millis < i64::MAX / 2, "implausible clock value"); Prevention
- Never bypass try_new to call new_every directly with unvalidated durations
- Sanity-check custom/mock TimeProvider values in tests
- Monitor host clock sync (NTP) on servers running triggers
When it happens
Trigger: Calling `Schedule::new_every` (via `try_new` on an `Every` spec) when `(now_millis / duration_millis + 1) * duration_millis` overflows i64 milliseconds, or when the time provider returns a timestamp near i64::MAX — even though durations > 1 year are pre-filtered by the try_new check.
Common situations: A custom/mock TimeProvider returning huge or negative timestamps; a duration that passes the 1-year check but still overflows on multiplication; running with a corrupted system clock far in the future.
Related errors
- cannot fit duration into u64
- duration not to overflow
- duration not to overflow
- generation duration overflows u64 nanoseconds
- no overflow
AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19).
Data as JSON: /api/errors/cde13ed0c2aa214c.
Report an issue: GitHub.
Appendix: source
Thrown at influxdb3_processing_engine/src/plugins.rs:211
}
}
fn new_cron(cron_schedule: CronSchedule, time_provider: Arc<dyn TimeProvider>) -> Self {
let mut schedule = Box::new(cron_schedule.after_owned(time_provider.now().date_time()));
let next_trigger_time = schedule.next();
Self {
schedule: Schedule::Cron(schedule),
next_trigger_time,
}
}
fn new_every(duration: Duration, time_provider: Arc<dyn TimeProvider>) -> Self {
let now = time_provider.now().date_time();
let duration_millis = duration.num_milliseconds();
let now_millis = now.timestamp_millis();
let next_trigger_millis = ((now_millis / duration_millis) + 1) * duration_millis;
let next_trigger_time = Some(
DateTime::from_timestamp_millis(next_trigger_millis).expect("can't be out of range"),
);
Self {
schedule: Schedule::Every(duration),
next_trigger_time,
}
}
fn advance_time(&mut self) {
self.next_trigger_time = match &mut self.schedule {
Schedule::Cron(schedule) => schedule.next(),
Schedule::Every(duration) => self.next_trigger_time.map(|time| time + *duration),
};
}
/// A funky little method to get a tokio Instant that we can call `tokio::time::sleep_until()` on.
fn next_run_time(&self) -> Option<Time> {
let next_trigger_time = Time::from_datetime(*self.next_trigger_time.as_ref()?);
Some(next_trigger_time)View on GitHub (pinned to 06200ef96b)