cube-js/cube · error

Invalid timestamp: {}

Error message

Invalid timestamp: {}

What it means

to_naive_datetime converts a Timestamp's unix_nano field to a chrono NaiveDateTime via DateTime::from_timestamp. If the nanosecond count is outside the representable range (roughly years 1677–2262, the i64-nanosecond limit), from_timestamp returns None and the code panics with 'Invalid timestamp: <n>'.

Source

Thrown at rust/cubesql/pg-srv/src/values/timestamp.rs:44

pub struct TimestampValue {
    unix_nano: i64,
    tz: Option<String>,
}

impl TimestampValue {
    pub fn new(mut unix_nano: i64, tz: Option<String>) -> TimestampValue {
        // This is a hack to workaround a mismatch between on-disk and in-memory representations.
        // We use microsecond precision on-disk.
        unix_nano -= unix_nano % 1000;
        TimestampValue { unix_nano, tz }
    }

    pub fn to_naive_datetime(&self) -> NaiveDateTime {
        // Convert nanoseconds to seconds and nanoseconds
        let secs = self.unix_nano / 1_000_000_000;
        let nsecs = (self.unix_nano % 1_000_000_000) as u32;
        DateTime::from_timestamp(secs, nsecs)
            .unwrap_or_else(|| panic!("Invalid timestamp: {}", self.unix_nano))
            .naive_utc()
    }

    pub fn to_fixed_datetime(&self) -> io::Result<DateTime<Tz>> {
        assert!(self.tz.is_some());
        let tz = self
            .tz
            .as_ref()
            .unwrap()
            .parse::<Tz>()
            .map_err(|err| io::Error::other(err.to_string()))?;
        let ndt = self.to_naive_datetime();
        Ok(tz.from_utc_datetime(&ndt))
    }

    pub fn tz_ref(&self) -> &Option<String> {
        &self.tz
    }

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Fix the data or the query to return timestamps within the supported range (approximately 1677-2262)
  2. Check unit conversion upstream — the value is likely seconds or milliseconds mistaken for nanoseconds
  3. Clamp or sanitize sentinel values (e.g. '9999-12-31') before they reach CubeSQL

Example fix

// before
SELECT expire_at FROM tokens -- expire_at = 9999-12-31
// after
SELECT CAST(expire_at AS TIMESTAMP) AS expire_at FROM tokens -- keep values within 1677-2262
Defensive patterns

Strategy: validation

Validate before calling

const MIN_NANO: i64 = -9_223_286_400 * 1_000_000_000 / 10; // ~1677
const MAX_NANO: i64 = 9_223_286_400 * 1_000_000_000 / 10;  // ~2262
fn timestamp_in_range(unix_nano: i64) -> bool {
    (MIN_NANO..=MAX_NANO).contains(&unix_nano)
}
// check before converting bind params or column values
assert!(timestamp_in_range(ts.unix_nano), "timestamp out of chrono range");

Type guard

fn timestamp_in_range(unix_nano: i64) -> bool {
    // chrono NaiveDateTime range: ~1677-04-19 to ~2262-04-11
    (-9_223_286_400_000_000_000i64 / 10..=9_223_286_400_000_000_000i64 / 10).contains(&unix_nano)
}

Prevention

When it happens

Trigger: A timestamp value with unix_nano beyond the chrono-supported range is read from a data source or received as a bind parameter and converted via to_naive_datetime (also via to_fixed_datetime/to_text/to_binary).

Common situations: Columns storing epoch-seconds misinterpreted as nanoseconds (values ~1e18 too small/large); sentinel dates like year 0 or 9999; upstream systems writing far-future expiry timestamps.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/3a41c3a90d4b9fe7. Report an issue: GitHub.