python/cpython · error · ValueError

month must be in 1..12, not {month}

Error message

month must be in 1..12, not {month}

What it means

Raised by _check_date_fields() when the month argument to date()/datetime() is not an integer in 1..12. The argument is coerced via __index__ first, so 13, 0, or negative values fail after coercion. This is the standard calendar-range guard for the month field.

Source

Thrown at Lib/_pydatetime.py:575

    assert name in ("utcoffset", "dst")
    if offset is None:
        return
    if not isinstance(offset, timedelta):
        raise TypeError(f"tzinfo.{name}() must return None "
                        f"or timedelta, not {type(offset).__name__!r}")
    if not -timedelta(1) < offset < timedelta(1):
        raise ValueError("offset must be a timedelta "
                         "strictly between -timedelta(hours=24) and "
                         f"timedelta(hours=24), not {offset!r}")

def _check_date_fields(year, month, day):
    year = _index(year)
    month = _index(month)
    day = _index(day)
    if not MINYEAR <= year <= MAXYEAR:
        raise ValueError(f"year must be in {MINYEAR}..{MAXYEAR}, not {year}")
    if not 1 <= month <= 12:
        raise ValueError(f"month must be in 1..12, not {month}")
    dim = _days_in_month(year, month)
    if not 1 <= day <= dim:
        raise ValueError(f"day {day} must be in range 1..{dim} for month {month} in year {year}")
    return year, month, day

def _check_time_fields(hour, minute, second, microsecond, fold):
    hour = _index(hour)
    minute = _index(minute)
    second = _index(second)
    microsecond = _index(microsecond)
    if not 0 <= hour <= 23:
        raise ValueError(f"hour must be in 0..23, not {hour}")
    if not 0 <= minute <= 59:
        raise ValueError(f"minute must be in 0..59, not {minute}")
    if not 0 <= second <= 59:
        raise ValueError(f"second must be in 0..59, not {second}")
    if not 0 <= microsecond <= 999999:
        raise ValueError(f"microsecond must be in 0..999999, not {microsecond}")

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Convert 0-based months to 1-based (add 1) before constructing date
  2. Validate month in 1..12 when accepting user input
  3. Use dateutil.parser or datetime.strptime('%Y-%m-%d') to parse instead of manual splitting

Example fix

// before
d = date(2024, js_month, 1)  # js_month is 0..11
// after
d = date(2024, js_month + 1, 1)
Defensive patterns

Strategy: validation

Validate before calling

if not 1 <= month <= 12:
    raise ValueError('month must be 1..12')

Type guard

def valid_month(m) -> bool:
    return 1 <= m <= 12

Prevention

When it happens

Trigger: date(2024, 13, 1); date(2024, 0, 5); passing a 0-based month from another API (e.g. JavaScript Date getMonth() returns 0..11) directly to Python's date().

Common situations: Porting JS/Java code where months are 0-based; off-by-one after parsing month strings with a custom map; user-supplied form input like month=13.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/f4ed817f1490063a. Report an issue: GitHub.