{"record":{"id":"4ad876c7f1cd5ab0","repo":"influxdata/influxdb","slug":"invalid-year-month-format-0-expected-yyyy-mm","errorCode":null,"errorMessage":"invalid year-month format: {0}, expected YYYY-MM","messagePattern":"invalid year-month format: (.+?), expected YYYY-MM","errorType":"validation","errorClass":"YearMonthError","httpStatus":null,"severity":"warning","filePath":"influxdb3_write/src/lib.rs","lineNumber":429,"sourceCode":"}\n\n/// A year-month value in YYYY-MM format (e.g., \"2025-01\").\n///\n/// Used for organizing snapshot checkpoints by month. Stores year and month\n/// as integers for efficient comparison and sorting, while serializing to\n/// the standard \"YYYY-MM\" string format for backward compatibility.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct YearMonth {\n    year: u16,\n    month: u8,\n}\n\n/// Error type for invalid YearMonth values.\n#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]\npub enum YearMonthError {\n    #[error(\"invalid month {0}, expected 1-12\")]\n    InvalidMonth(u8),\n    #[error(\"invalid year-month format: {0}, expected YYYY-MM\")]\n    InvalidFormat(String),\n}\n\nimpl YearMonth {\n    /// Create a YearMonth from year and month without validation.\n    ///\n    /// # Safety\n    /// Use only when the values are known to be valid (e.g., from chrono).\n    /// In debug builds, panics if month is not in 1-12.\n    pub fn new_unchecked(year: u16, month: u8) -> Self {\n        debug_assert!((1..=12).contains(&month), \"month must be 1-12\");\n        Self { year, month }\n    }\n\n    /// Get the year component.\n    pub fn year(&self) -> u16 {\n        self.year\n    }","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_write/src/lib.rs#L411-L447","documentation":"YearMonthError::InvalidFormat is returned when parsing a year-month string that does not match the expected YYYY-MM format. The offending input is included in the message. YearMonth parsing is strict so retention/window keys remain unambiguous and sortable.","triggerScenarios":"Calling the YearMonth FromStr/parse implementation with strings like \"2026/01\", \"26-1\", \"2026-1\", empty strings, or values with extra whitespace/characters.","commonSituations":"Users writing retention configs with slash-separated dates or missing zero padding; API clients sending locale-formatted months; scripts generating dates with inconsistent formatting.","solutions":["Format the input as exactly YYYY-MM (zero-padded two-digit month) before parsing","Parse with an explicit format first (e.g. chrono \"%Y-%m\") and construct YearMonth from the parsed parts","Trim/normalize user input and reject clearly formatted values with a friendly message upstream","Check for locale-specific date formatting leaking into the config value"],"exampleFix":"// before\nlet ym: YearMonth = \"2026/1\".parse()?; // InvalidFormat\n// after\nlet ym: YearMonth = \"2026-01\".parse()?;","handlingStrategy":"validation","validationCode":"fn valid_ym_str(s: &str) -> bool {\n    let b = s.as_bytes();\n    b.len() == 7 && b[4] == b'-' && s[..4].bytes().all(|c| c.is_ascii_digit())\n        && s[5..].bytes().all(|c| c.is_ascii_digit())\n}","typeGuard":"fn looks_like_yyyy_mm(s: &str) -> bool {\n    s.len() == 7 && s.as_bytes()[4] == b'-' && s.split('-').count() == 2\n}","tryCatchPattern":"match s.parse::<YearMonth>() {\n    Err(YearMonthError::InvalidFormat(raw)) => {\n        eprintln!(\"{:?} is not YYYY-MM; normalizing...\", raw);\n        chrono::NaiveDate::parse_from_str(&raw, \"%Y/%m\")\n            .map(|d| YearMonth::new(d.year(), d.month() as u8).unwrap())?\n    }\n    r => r?,\n}","preventionTips":["Normalize all date inputs with chrono (\"%Y-%m\") before parsing into YearMonth","Zero-pad months in generated configs and scripts","Validate retention settings when loading config, not at use time","Document the YYYY-MM requirement next to the config field"],"tags":["rust","validation","date-format"],"backgroundTag":"invalid-date-format","analyzedSha":"06200ef96ba82c5f6727e5038a83af8e722c6875","analyzedAt":"2026-09-19T12:55:30.003Z","contentChangedAt":"2026-09-19T12:55:30.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}