vectordotdev/vector · critical

Timestamp out of range

Error message

Timestamp out of range

What it means

Panic via `.expect("Timestamp out of range")` in `Transformer::apply_timestamp_format` (lib/codecs/src/encoding/transformer.rs). When the codec's `timestamp_format = "unix_ns"`, each event timestamp is converted with `ts.timestamp_nanos_opt()`, which returns `None` when the instant cannot be represented as an i64 count of nanoseconds — roughly dates before 1678 or after 2262. The `expect` then panics inside the encoding pipeline, taking down the process mid-stream.

Source

Thrown at lib/codecs/src/encoding/transformer.rs:212

            let timestamp = if let Value::Timestamp(ts) = log.value() {
                Some(extract(ts))
            } else {
                None
            };
            if let Some(ts) = timestamp {
                log.insert(&vrl::path::OwnedTargetPath::event_root(), ts.into());
            }
        }
    }

    fn apply_timestamp_format(&self, log: &mut LogEvent) {
        if let Some(timestamp_format) = self.timestamp_format.as_ref() {
            match timestamp_format {
                TimestampFormat::Unix => self.format_timestamps(log, |ts| ts.timestamp()),
                TimestampFormat::UnixMs => self.format_timestamps(log, |ts| ts.timestamp_millis()),
                TimestampFormat::UnixUs => self.format_timestamps(log, |ts| ts.timestamp_micros()),
                TimestampFormat::UnixNs => self.format_timestamps(log, |ts| {
                    ts.timestamp_nanos_opt().expect("Timestamp out of range")
                }),
                TimestampFormat::UnixFloat => self.format_timestamps(log, |ts| {
                    NotNan::new(ts.timestamp_micros() as f64 / 1e6)
                        .expect("this division will never produce a NaN")
                }),
                // RFC3339 is the default serialization of a timestamp.
                TimestampFormat::Rfc3339 => (),
            }
        }
    }

    /// Set the `except_fields` value.
    ///
    /// Returns `Err` if the new `except_fields` fail validation, i.e. are not mutually exclusive
    /// with `only_fields`.
    #[cfg(any(test, feature = "test"))]
    pub fn set_except_fields(
        &mut self,

View on GitHub (pinned to 3708c39b12)

Solutions

  1. Use a coarser format with wider range — `unix` (seconds), `unix_ms`, or `unix_us` — or `rfc3339`
  2. Sanitize/clamp event timestamps in a VRL remap transform before the sink (e.g. bound them to 1970–2262)
  3. Fix the upstream producer or parsing rule that creates out-of-range timestamps (two-digit-year syslog is a classic source)

Example fix

# before
[sinks.out.encoding]
timestamp_format = "unix_ns"

# after
[sinks.out.encoding]
timestamp_format = "unix_us"
Defensive patterns

Strategy: validation

Validate before calling

// Guard before encoding: only unix_ns-encodable timestamps reach the sink
fn nanos_representable(ts: chrono::DateTime<chrono::Utc>) -> bool {
    ts.timestamp_nanos_opt().is_some()
}

Type guard

fn nanos_representable(ts: chrono::DateTime<chrono::Utc>) -> bool {
    ts.timestamp_nanos_opt().is_some() // roughly 1678..=2262
}

Prevention

When it happens

Trigger: An encoding config with `timestamp_format = "unix_ns"` plus an event whose timestamp falls outside the nanosecond-representable range: year 9999 test fixtures, sentinel/zero dates, or RFC3164 syslog timestamps with two-digit years resolved into the wrong century (e.g. interpreted as 2108+).

Common situations: Switching a sink's codec to `unix_ns` for a consumer that wants nanosecond precision, then ingesting malformed or far-future timestamps from test data, misconfigured device clocks, or date-parsing bugs upstream.

Related errors


AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20). Data as JSON: /api/errors/bba37c92ede0be16. Report an issue: GitHub.