python/cpython · error · ValueError
year must be in {MINYEAR}..{MAXYEAR}, not {year}
Error message
year must be in {MINYEAR}..{MAXYEAR}, not {year} What it means
_isoweek_to_gregorian (backing date.fromisocalendar and the ISO-week parse path of fromisoformat) requires the ISO year within datetime.MINYEAR..MAXYEAR (1..9999) because week 1 of an ISO year can resolve into the previous Gregorian year. Out-of-range years raise ValueError with the allowed range echoed.
Source
Thrown at Lib/_pydatetime.py:515
_check_time_fields(hour=tz_comps[0], minute=tz_comps[1],
second=tz_comps[2], microsecond=tz_comps[3],
fold=0)
except ValueError as e:
error_from_tz = e
else:
td = timedelta(hours=tz_comps[0], minutes=tz_comps[1],
seconds=tz_comps[2], microseconds=tz_comps[3])
tzi = timezone(tzsign * td)
time_comps.append(tzi)
return time_comps, became_next_day, error_from_components, error_from_tz
# tuple[int, int, int] -> tuple[int, int, int] version of date.fromisocalendar
def _isoweek_to_gregorian(year, week, day):
# Year is bounded this way because 9999-12-31 is (9999, 52, 5)
if not MINYEAR <= year <= MAXYEAR:
raise ValueError(f"year must be in {MINYEAR}..{MAXYEAR}, not {year}")
if not 0 < week < 53:
out_of_range = True
if week == 53:
# ISO years have 53 weeks in them on years starting with a
# Thursday and leap years starting on a Wednesday
first_weekday = _ymd2ord(year, 1, 1) % 7
if (first_weekday == 4 or (first_weekday == 3 and
_is_leap(year))):
out_of_range = False
if out_of_range:
raise ValueError(f"Invalid week: {week}")
if not 0 < day < 8:
raise ValueError(f"Invalid weekday: {day} (range is [1, 7])")
View on GitHub (pinned to bc6749cc3b)
Solutions
- Clamp or reject years outside 1..9999 before calling fromisocalendar
- Fix upstream year expansion (century windowing) so two-digit years map into range
- Use try/except ValueError around calendar conversion at the trust boundary and report the bad row/record
Example fix
// before
d = date.fromisocalendar(year, week, day) # year 0 -> ValueError
# after
if not 1 <= year <= 9999:
raise ValueError(f'bad ISO year {year!r} in record')
d = date.fromisocalendar(year, week, day) Defensive patterns
Strategy: validation
Validate before calling
from datetime import MINYEAR, MAXYEAR
def check_iso_year(year: int) -> None:
if not MINYEAR <= year <= MAXYEAR:
raise ValueError(f'ISO year {year} outside {MINYEAR}..{MAXYEAR}') Type guard
def is_valid_iso_year(y: object) -> bool:
return isinstance(y, int) and 1 <= y <= 9999 Try / catch
try:
d = date.fromisocalendar(y, w, wd)
except ValueError as e:
raise ValueError(f'invalid calendar input {y}/{w}/{wd}: {e}') from e Prevention
- Sanitize imported years (0, 1899, 10000 are classic bad data)
- Expand two-digit years with an explicit century window
- Wrap calendar conversions at the data boundary with row-level error reporting
When it happens
Trigger: date.fromisocalendar(0, 1, 1) or (10000, 1, 1); datetime.fromisoformat('10000-W01-1') reaching the week path; user-supplied year 0 from a spreadsheet or form used directly as the ISO year.
Common situations: Excel/CSV artifacts exporting year 0 or 1899; two-digit years expanded wrongly ('99' -> 99 not 1999); edge-year tests probing calendar boundaries; default sentinel 0 flowing into fromisocalendar.
Related errors
- Invalid week: {week}
- Invalid weekday: {day} (range is [1, 7])
- Unknown timespec value
- Invalid ISO string
- Invalid isoformat string
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/1ca87d0550a18240.
Report an issue: GitHub.