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
- Use a coarser format with wider range — `unix` (seconds), `unix_ms`, or `unix_us` — or `rfc3339`
- Sanitize/clamp event timestamps in a VRL remap transform before the sink (e.g. bound them to 1970–2262)
- 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
- Prefer unix_us/unix_ms or rfc3339 unless you truly need nanosecond precision
- Clamp or drop out-of-range timestamps in a VRL remap before a unix_ns sink
- Watch for two-digit-year (RFC3164) timestamps resolving into the far future; fix parsing upstream
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
- skipping more than 2^64 events at a time is obviously a bug
- completing more than 2^16 data files at a time is obviously
- path and query should never fail to parse
- Serializer does not support JSON
- Paths must always start with a leading forward slash (`/`).
AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20).
Data as JSON: /api/errors/bba37c92ede0be16.
Report an issue: GitHub.