{"record":{"id":"5de42d16a6198ca9","repo":"risingwavelabs/risingwave","slug":"0","errorCode":null,"errorMessage":"{0}","messagePattern":"\\{0\\}","errorType":"validation","errorClass":"IntervalParseError","httpStatus":null,"severity":"error","filePath":"src/common/src/types/interval.rs","lineNumber":1027,"sourceCode":"        }\n    }\n}\n\n/// Error type for parsing an [`Interval`].\n#[derive(thiserror::Error, Debug, thiserror_ext::Construct)]\npub enum IntervalParseError {\n    #[error(\"Invalid interval: {0}\")]\n    Invalid(String),\n\n    #[error(\n        \"Invalid interval: {0}, expected format P<years>Y<months>M<days>DT<hours>H<minutes>M<seconds>S\"\n    )]\n    InvalidIso8601(String),\n\n    #[error(\"Invalid unit: {0}\")]\n    InvalidUnit(String),\n\n    #[error(\"{0}\")]\n    Uncategorized(String),\n}\n\ntype ParseResult<T> = std::result::Result<T, IntervalParseError>;\n\nimpl Interval {\n    pub fn as_iso_8601(&self) -> String {\n        // ISO pattern - PnYnMnDTnHnMnS\n        let years = self.months / 12;\n        let months = self.months % 12;\n        let days = self.days;\n        let secs_fract = (self.usecs % USECS_PER_SEC).abs();\n        let total_secs = (self.usecs / USECS_PER_SEC).abs();\n        let hours = total_secs / 3600;\n        let minutes = (total_secs / 60) % 60;\n        let seconds = total_secs % 60;\n        let mut buf = [0u8; 7];\n        let fract_str = if secs_fract != 0 {","sourceCodeStart":1009,"sourceCodeEnd":1045,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/common/src/types/interval.rs#L1009-L1045","documentation":"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').","triggerScenarios":"Interval strings containing punctuation other than '-', '+', '.', ':', digits, letters and whitespace: e.g. '1h 30m!' , '2 days, 3 hours', parentheses, or non-ASCII unit names.","commonSituations":"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.","solutions":["Remove unsupported characters; only digits, ASCII letters, whitespace, '-', '+', '.', ':' are allowed.","Replace comma thousand-separators with plain digits.","Sanitize/trim the input string before parsing if it comes from user text."],"exampleFix":"// before\nlet iv: Interval = \"2 days, 3 hours\".parse()?;\n// after\nlet iv: Interval = \"2 days 3 hours\".parse()?;","handlingStrategy":"validation","validationCode":"fn has_only_allowed_chars(s: &str) -> bool {\n    s.chars().all(|c| c.is_ascii_alphanumeric() || c.is_ascii_whitespace() || matches!(c, '-' | '+' | '.' | ':'))\n}","typeGuard":"fn is_clean_interval_string(s: &str) -> bool {\n    !s.is_empty() && s.is_ascii() && s.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '+' | '.' | ':' | ' '))\n}","tryCatchPattern":"let iv = Interval::from_str(s)\n    .map_err(|e| anyhow!(\"cannot tokenize interval '{s}': {e}\"))?;","preventionTips":["Sanitize user-supplied interval text: remove commas, brackets, punctuation.","Check inputs are ASCII - unicode lookalikes are rejected by the tokenizer.","Reject or clean interval strings at the application boundary before SQL construction."],"tags":["rust","interval","parsing","tokenizer"],"backgroundTag":"invalid-argument-format","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}