vectordotdev/vector · critical

invalid timestamp

Error message

invalid timestamp

What it means

The journald source consumes the journalctl export format; timestamp fields such as __REALTIME_TIMESTAMP arrive as microsecond strings parsed to u64, then chrono::Utc.timestamp_opt(ts / 1_000_000, ts % 1_000_000 * 1_000).single().expect("invalid timestamp"). Microsecond values beyond chrono's representable window (about 8.2e18 microseconds, year ~262143) make single() return None and the expect panics the source task.

Source

Thrown at src/sources/journald.rs:883

        LogNamespace::Vector => log
            .get(metadata_path!(JournaldConfig::NAME, "metadata"))
            .and_then(|meta| {
                meta.get(path!(SOURCE_TIMESTAMP))
                    .or_else(|| meta.get(path!(RECEIVED_TIMESTAMP)))
            }),
        LogNamespace::Legacy => log
            .get(event_path!(SOURCE_TIMESTAMP))
            .or_else(|| log.get(event_path!(RECEIVED_TIMESTAMP))),
    };

    let timestamp = timestamp_value
        .filter(|&ts| ts.is_bytes())
        .and_then(|ts| ts.as_str().unwrap().parse::<u64>().ok())
        .map(|ts| {
            chrono::Utc
                .timestamp_opt((ts / 1_000_000) as i64, (ts % 1_000_000) as u32 * 1_000)
                .single()
                .expect("invalid timestamp")
        });

    // Add timestamp.
    match log_namespace {
        LogNamespace::Vector => {
            log.insert(metadata_path!("vector", "ingest_timestamp"), Utc::now());

            if let Some(ts) = timestamp {
                log.insert(metadata_path!(JournaldConfig::NAME, "timestamp"), ts);
            }
        }
        LogNamespace::Legacy => {
            if let Some(ts) = timestamp {
                log.maybe_insert(log_schema().timestamp_key_target_path(), ts);
            }
        }
    }

View on GitHub (pinned to 3708c39b12)

Solutions

  1. Sanitize or bound the producer's timestamp fields before they reach Vector
  2. Patch: match on .single() (or pre-filter ts) and skip events with out-of-range timestamps, with a warning
  3. Upgrade Vector once graceful handling lands

Example fix

// before
.map(|ts| {
    chrono::Utc
        .timestamp_opt((ts / 1_000_000) as i64, (ts % 1_000_000) as u32 * 1_000)
        .single()
        .expect("invalid timestamp")
});

// after
.and_then(|ts| {
    chrono::Utc
        .timestamp_opt((ts / 1_000_000) as i64, (ts % 1_000_000) as u32 * 1_000)
        .single()
})
// and, if preferred, log when the result is None instead of silently dropping
Defensive patterns

Strategy: validation

Validate before calling

const MAX_VALID_MICROS: u64 = 8_210_266_876_799_000_000; // ~chrono max

fn valid_journald_micros(ts: u64) -> bool {
    ts <= MAX_VALID_MICROS
}

// before parsing:
if !valid_journald_micros(ts) {
    warn!(message = "dropping event with out-of-range journal timestamp", ts);
    return None;
}

Type guard

fn journal_micros_to_datetime(ts: u64) -> Option<chrono::DateTime<chrono::Utc>> {
    chrono::Utc
        .timestamp_opt((ts / 1_000_000) as i64, (ts % 1_000_000) as u32 * 1_000)
        .single()
}

Try / catch

let timestamp = timestamp_value.and_then(|ts| {
    chrono::Utc
        .timestamp_opt((ts / 1_000_000) as i64, (ts % 1_000_000) as u32 * 1_000)
        .single()
});
// None: proceed without a source timestamp and log at debug/warn

Prevention

When it happens

Trigger: Feeding the journald source a crafted or corrupt export stream (or HTTP/gRPC payload in that format) whose timestamp field is a huge u64 such as u64::MAX, or placeholder/sentinel test values; healthy systemd journals never produce them.

Common situations: Replaying captured or fuzzed journal exports, custom journald forwarders, test fixtures with sentinel timestamps.

Related errors


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