risingwavelabs/risingwave · error · IntervalParseError

{0}

Error message

{0}

What it means

IntervalParseError::Uncategorized carries a free-form message for tokenizer-level failures in parse_interval that don't fit the other variants - notably an illegal character at a given offset ('Invalid character at offset N in <s>: <c>. Only support digit or alphabetic now').

Source

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

        }
    }
}

/// 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 {
        // ISO pattern - PnYnMnDTnHnMnS
        let years = self.months / 12;
        let months = self.months % 12;
        let days = self.days;
        let secs_fract = (self.usecs % USECS_PER_SEC).abs();
        let total_secs = (self.usecs / USECS_PER_SEC).abs();
        let hours = total_secs / 3600;
        let minutes = (total_secs / 60) % 60;
        let seconds = total_secs % 60;
        let mut buf = [0u8; 7];
        let fract_str = if secs_fract != 0 {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Remove unsupported characters; only digits, ASCII letters, whitespace, '-', '+', '.', ':' are allowed.
  2. Replace comma thousand-separators with plain digits.
  3. Sanitize/trim the input string before parsing if it comes from user text.

Example fix

// before
let iv: Interval = "2 days, 3 hours".parse()?;
// after
let iv: Interval = "2 days 3 hours".parse()?;
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: Interval strings containing punctuation other than '-', '+', '.', ':', digits, letters and whitespace: e.g. '1h 30m!' , '2 days, 3 hours', parentheses, or non-ASCII unit names.

Common situations: Copy-pasted interval text with commas or trailing punctuation; templated SQL injecting formatted numbers ('1,000.5 hours'); unicode lookalike characters from rich-text sources.

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/5de42d16a6198ca9. Report an issue: GitHub.