influxdata/influxdb · warning · YearMonthError

invalid year-month format

Error message

invalid year-month format: {0}, expected YYYY-MM

What it means

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.

Solutions

  1. Format the input as exactly YYYY-MM (zero-padded two-digit month) before parsing
  2. Parse with an explicit format first (e.g. chrono "%Y-%m") and construct YearMonth from the parsed parts
  3. Trim/normalize user input and reject clearly formatted values with a friendly message upstream
  4. Check for locale-specific date formatting leaking into the config value

Example fix

// before
let ym: YearMonth = "2026/1".parse()?; // InvalidFormat
// after
let ym: YearMonth = "2026-01".parse()?;
Defensive patterns

Strategy: validation

Validate before calling

fn valid_ym_str(s: &str) -> bool {
    let b = s.as_bytes();
    b.len() == 7 && b[4] == b'-' && s[..4].bytes().all(|c| c.is_ascii_digit())
        && s[5..].bytes().all(|c| c.is_ascii_digit())
}

Type guard

fn looks_like_yyyy_mm(s: &str) -> bool {
    s.len() == 7 && s.as_bytes()[4] == b'-' && s.split('-').count() == 2
}

Try / catch

match s.parse::<YearMonth>() {
    Err(YearMonthError::InvalidFormat(raw)) => {
        eprintln!("{:?} is not YYYY-MM; normalizing...", raw);
        chrono::NaiveDate::parse_from_str(&raw, "%Y/%m")
            .map(|d| YearMonth::new(d.year(), d.month() as u8).unwrap())?
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling the YearMonth FromStr/parse implementation with strings like "2026/01", "26-1", "2026-1", empty strings, or values with extra whitespace/characters.

Common situations: Users writing retention configs with slash-separated dates or missing zero padding; API clients sending locale-formatted months; scripts generating dates with inconsistent formatting.

Related errors


AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19). Data as JSON: /api/errors/4ad876c7f1cd5ab0. Report an issue: GitHub.

Appendix: source

Thrown at influxdb3_write/src/lib.rs:429

}

/// A year-month value in YYYY-MM format (e.g., "2025-01").
///
/// Used for organizing snapshot checkpoints by month. Stores year and month
/// as integers for efficient comparison and sorting, while serializing to
/// the standard "YYYY-MM" string format for backward compatibility.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct YearMonth {
    year: u16,
    month: u8,
}

/// Error type for invalid YearMonth values.
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum YearMonthError {
    #[error("invalid month {0}, expected 1-12")]
    InvalidMonth(u8),
    #[error("invalid year-month format: {0}, expected YYYY-MM")]
    InvalidFormat(String),
}

impl YearMonth {
    /// Create a YearMonth from year and month without validation.
    ///
    /// # Safety
    /// Use only when the values are known to be valid (e.g., from chrono).
    /// In debug builds, panics if month is not in 1-12.
    pub fn new_unchecked(year: u16, month: u8) -> Self {
        debug_assert!((1..=12).contains(&month), "month must be 1-12");
        Self { year, month }
    }

    /// Get the year component.
    pub fn year(&self) -> u16 {
        self.year
    }

View on GitHub (pinned to 06200ef96b)