risingwavelabs/risingwave · error · IntervalParseError

Invalid interval: {0}

Error message

Invalid interval: {0}

What it means

IntervalParseError::Invalid is the generic failure of Interval's PostgreSQL-style text parser: the token stream did not yield a valid (years, months, days, usecs) tuple. Raised by parse_interval when a numeric buffer can't parse as i64 (convert_digit), an 'HH:MM:SS' segment is malformed, or the ':' appears without a preceding digit.

Source

Thrown at src/common/src/types/interval.rs:1016

}

impl ToText for crate::types::Interval {
    fn write<W: std::fmt::Write>(&self, f: &mut W) -> std::fmt::Result {
        write!(f, "{self}")
    }

    fn write_with_type<W: std::fmt::Write>(&self, ty: &DataType, f: &mut W) -> std::fmt::Result {
        match ty {
            DataType::Interval => self.write(f),
            _ => unreachable!(),
        }
    }
}

/// Error type for parsing an [`Interval`].
#[derive(thiserror::Error, Debug, thiserror_ext::Construct)]
pub enum IntervalParseError {
    #[error("Invalid interval: {0}")]
    Invalid(String),

    #[error(
        "Invalid interval: {0}, expected format P<years>Y<months>M<days>DT<hours>H<minutes>M<seconds>S"
    )]
    InvalidIso8601(String),

    #[error("Invalid unit: {0}")]
    InvalidUnit(String),

    #[error("{0}")]
    Uncategorized(String),
}

type ParseResult<T> = std::result::Result<T, IntervalParseError>;

impl Interval {
    pub fn as_iso_8601(&self) -> String {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Follow PostgreSQL interval syntax: '[<n> years] [<n> mons] [<n> days] [HH:MM:SS]'.
  2. Remove unsupported characters (commas, brackets); use space or unit-letter separators.
  3. For machine-readable exchange, use ISO 8601 duration format (P1Y2M3DT4H5M6S) which gets a more specific error.

Example fix

// before
let iv: Interval = "1,000 days".parse()?;
// after
let iv: Interval = "1000 days".parse()?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_pg_style_interval(s: &str) -> bool {
    s.split_whitespace().all(|tok| {
        tok.chars().next().map(|c| c.is_ascii_digit() || c == '-' || c == '+' || c == ':' || c.is_ascii_alphabetic()).unwrap_or(false)
    })
}

Type guard

fn looks_like_interval(s: &str) -> bool {
    !s.trim().is_empty()
        && s.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '+' | '.' | ':' | ' '))
}

Try / catch

let iv = Interval::from_str(s)
    .map_err(|e| anyhow!("invalid interval '{s}': {e}"))?;

Prevention

When it happens

Trigger: '1 year 2 foo' (unknown trailing token), 'abc' (non-numeric), ':' at start (' :30'), malformed clock part ('1:2:3:4'), or huge numbers overflowing i64 via Interval::from_str / parse_interval.

Common situations: Hand-written SQL intervals with typos ('2 weaks'); copying intervals from other systems whose syntax differs; values with separators the tokenizer doesn't accept (commas: '1,000 days').

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/bc8319c8b2a5cdc2. Report an issue: GitHub.