risingwavelabs/risingwave · error · InvalidParamsError

Invalid time: {value} {unit} is out of range for a time of d

Error message

Invalid time: {value} {unit} is out of range for a time of day

What it means

ErrorKind::TimeOfDay in risingwave_common::types::datetime is raised when a nanosecond or microsecond count since midnight is too large to represent a valid TIME value (>= 86,400 seconds per day). Time::with_nano and Time::with_micro reject such values so that the TIME cast stays lossless. It surfaces as an InvalidParamsError.

Source

Thrown at src/common/src/types/datetime.rs:253

/// let time = Time::from(interval);
/// assert_eq!(time, Time::from_str("23:58:58.999997").unwrap());
/// ```
impl From<Interval> for Time {
    fn from(interval: Interval) -> Self {
        let usecs = interval.usecs_of_day();
        let secs = (usecs / 1_000_000) as u32;
        let nano = (usecs % 1_000_000 * 1000) as u32;
        Time::from_num_seconds_from_midnight_uncheck(secs, nano)
    }
}

#[derive(Copy, Clone, Debug, Error)]
enum ErrorKind {
    #[error("Invalid date: days: {days}")]
    Date { days: i32 },
    #[error("Invalid time: secs: {secs}, nanoseconds: {nsecs}")]
    Time { secs: u32, nsecs: u32 },
    #[error("Invalid time: {value} {unit} is out of range for a time of day")]
    TimeOfDay { value: u64, unit: &'static str },
    #[error("Invalid datetime: seconds: {secs}, nanoseconds: {nsecs}")]
    DateTime { secs: i64, nsecs: u32 },
    #[error("Invalid datetime: {value} {unit} is out of range")]
    Timestamp { value: i64, unit: &'static str },
    #[error("Can't cast string to date (expected format is YYYY-MM-DD)")]
    ParseDate,
    #[error(
        "Can't cast string to time (expected format is HH:MM:SS[.D+{{up to 6 digits}}][Z] or HH:MM)"
    )]
    ParseTime,
    #[error(
        "Can't cast string to timestamp (expected format is YYYY-MM-DD HH:MM:SS[.D+{{up to 9 digits}}] or YYYY-MM-DD HH:MM or YYYY-MM-DD or ISO 8601 format)"
    )]
    ParseTimestamp,
}

#[derive(Debug, Error)]

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Check the value is < 86_400 * 1_000_000 (micros) or < 86_400 * 1_000_000_000 (nanos) before constructing the Time.
  2. If the value is a duration, keep it as an Interval instead of casting to TIME.
  3. Wrap midnight overflow with modulo (value % MICROS_PER_DAY) if wrapping semantics are intended.

Example fix

// before
let t = Time::with_micro(micros_since_midnight)?;
// after
let t = Time::with_micro(micros_since_midnight % 86_400_000_000)?;
Defensive patterns

Strategy: validation

Validate before calling

const MICROS_PER_DAY: u64 = 86_400 * 1_000_000;
fn can_be_time_micros(micros: u64) -> bool { micros < MICROS_PER_DAY }

Type guard

fn is_valid_time_of_day(value: u64, unit_us: bool) -> bool {
    if unit_us { value < 86_400_000_000 } else { value < 86_400_000_000_000 }
}

Try / catch

match Time::with_micro(micros) {
    Ok(t) => t,
    Err(e) => { log::warn!("time-of-day overflow: {e}"); Time::with_micro(micros % 86_400_000_000).unwrap() }
}

Prevention

When it happens

Trigger: Calling Time::with_nano(nano) with nano >= 86_400 * 1_000_000_000, or Time::with_micro(micro) with micro >= 86_400 * 1_000_000; also hit indirectly when deserializing a Time column from protobuf (Time::from_protobuf) with a corrupt or out-of-range buffer.

Common situations: Ingesting source data where a duration-like column (elapsed ms/us) is accidentally cast to TIME; arithmetic on times that overflows midnight; corrupted or misaligned protobuf payloads on the wire.

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 risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/ffb7fd0a8ce35820. Report an issue: GitHub.