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

  1. Verify the duration passed is at most 1 year (the try_new guard) — avoid constructing `new_every` directly with unvalidated durations.
  2. Fix or replace the TimeProvider mock so `now()` returns a realistic timestamp.
  3. If you control the code, replace `.expect` with a fallible conversion that returns an error instead of panicking.
  4. 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

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


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)