vectordotdev/vector · error

interval_ms validated to fit in i64 in Aggregate::new

Error message

interval_ms validated to fit in i64 in Aggregate::new

What it means

`bucket_key` converts the configured `interval_ms` (u64) to i64 with `try_from` and expects success, relying on validation in `Aggregate::new`. A value above `i64::MAX` panics here. The conversion must be i64 because `div_euclid` operates on i64 timestamps.

Solutions

  1. Use a realistic `interval_ms` (millisecond-scale window sizes)
  2. Ensure `Aggregate::new` validates `interval_ms` fits in i64 and rejects the config otherwise
  3. Run `vector validate` before deployment
  4. File a bug if a reasonable interval triggers the panic
Defensive patterns

Strategy: validation

Validate before calling

if cfg.interval_ms > i64::MAX as u64 {
    return Err("interval_ms too large".into());
}

Prevention

When it happens

Trigger: An Aggregate whose `interval_ms` exceeds `i64::MAX` reaches `bucket_key` (via `record_event_time`) without constructor validation.

Common situations: Absurdly large interval values in config on versions/forks lacking constructor validation; direct unit-test construction of Aggregate with unvalidated config.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16). Data as JSON: /api/errors/02bdf386c6d46edd. Report an issue: GitHub.

Appendix: source

Thrown at src/transforms/aggregate/event_time.rs:118

            "only storable metrics reach event-time bucketing"
        );
        self.record_into_bucket(bucket_key, series, data, metadata);
        emit!(AggregateEventRecorded);
        None
    }

    /// Start of the half-open window `[bucket_key, bucket_key + interval_ms)` containing
    /// `timestamp`, aligned to multiples of `interval_ms` from the Unix epoch.
    ///
    /// Euclidean division (`div_euclid`) is required: Rust's truncating `/`
    /// rounds toward zero, so timestamps just before the epoch (negative
    /// millis) would incorrectly map into the non-negative bucket `[0, interval)`
    /// instead of `[-interval, 0)`.
    pub(crate) fn bucket_key(&self, timestamp: DateTime<Utc>) -> BucketKey {
        let timestamp_ms = timestamp.timestamp_millis();
        // Range-validated in `Aggregate::new` to fit in i64.
        let interval_ms = i64::try_from(self.config.interval_ms)
            .expect("interval_ms validated to fit in i64 in Aggregate::new");
        timestamp_ms
            .div_euclid(interval_ms)
            .saturating_mul(interval_ms)
    }

    /// Returns `true` if `bucket_key` belongs to a window that has already
    /// been emitted and therefore must not accept any further events.
    ///
    /// `watermark` is the *exclusive end* of the highest bucket flushed so
    /// far -- equivalently, the smallest `bucket_key` that is still valid to
    /// record into. `allowed_lateness_ms` is honoured at flush time (it
    /// delays closing the bucket); once a window has been emitted it is
    /// closed unconditionally and late events for it are dropped.
    const fn was_bucket_flushed(&self, bucket_key: BucketKey) -> bool {
        if let Some(watermark) = self.watermark {
            bucket_key < watermark
        } else {
            false

View on GitHub (pinned to bdb87aeaa4)