python/cpython · error · ValueError
Invalid ISO string
Error message
Invalid ISO string
What it means
Raised while locating the date/time separator in an ISO 8601 string during datetime.fromisoformat (the pure-Python fallback path _find_isoformat_datetime_separator). It fires for week-date strings of the form YYYY-Www that are truncated at exactly 8 characters or shorter where a separator must follow, i.e. structurally ambiguous/malformed week-date prefixes such as '2021-W53-' (length 9) or strings cut at the week number where the parser still expects a day component separator.
Source
Thrown at Lib/_pydatetime.py:314
# Helpers for parsing the result of isoformat()
def _is_ascii_digit(c):
return c in "0123456789"
def _find_isoformat_datetime_separator(dtstr):
# See the comment in _datetimemodule.c:_find_isoformat_datetime_separator
len_dtstr = len(dtstr)
if len_dtstr == 7:
return 7
assert len_dtstr > 7
date_separator = "-"
week_indicator = "W"
if dtstr[4] == date_separator:
if dtstr[5] == week_indicator:
if len_dtstr < 8:
raise ValueError("Invalid ISO string")
if len_dtstr > 8 and dtstr[8] == date_separator:
if len_dtstr == 9:
raise ValueError("Invalid ISO string")
if len_dtstr > 10 and _is_ascii_digit(dtstr[10]):
# This is as far as we need to resolve the ambiguity for
# the moment - if we have YYYY-Www-##, the separator is
# either a hyphen at 8 or a number at 10.
#
# We'll assume it's a hyphen at 8 because it's way more
# likely that someone will use a hyphen as a separator than
# a number, but at this point it's really best effort
# because this is an extension of the spec anyway.
# TODO(pganssle): Document this
return 8
return 10
else:
# YYYY-Www (8)
return 8View on GitHub (pinned to bc6749cc3b)
Solutions
- Validate/complete the timestamp before parsing: ISO week dates should be fully formed like '2021-W53-5' or use date.fromisocalendar(2021, 53, 5) instead of fromisoformat
- Strip trailing separators: s.rstrip('-') then re-check format
- Parse with a strict schema first (e.g. datetime.strptime(s, '%G-W%V-%u')) and fall back to fromisoformat
Example fix
// before
dt = datetime.fromisoformat('2021-W53-') # ValueError
# after
from datetime import date
d = date.fromisocalendar(2021, 53, 5) Defensive patterns
Strategy: validation
Validate before calling
import re
_ISO_WEEK_RE = re.compile(r'^\d{4}-W\d{2}-\d$')
def parse_week_date(s: str):
if not _ISO_WEEK_RE.fullmatch(s):
raise ValueError(f'not a complete ISO week date: {s!r}')
return datetime.fromisoformat(s) Type guard
def is_complete_iso_week_date(s: str) -> bool:
return len(s) == 10 and s[4] == '-' and s[5] == 'W' and s[8] == '-' Try / catch
try:
dt = datetime.fromisoformat(s)
except ValueError:
dt = None # log and skip malformed record Prevention
- Never feed truncated/sliced timestamps to fromisoformat
- Use date.fromisocalendar for week dates instead of string parsing
- Validate extracted substrings before parsing
When it happens
Trigger: datetime.fromisoformat('2021-W53-') (len 9 after the W-branch); week-date strings shorter than 8 chars once a 'W' at index 5 is seen; hand-built 'YYYY-Www' + partial separator strings passed to fromisoformat.
Common situations: Parsing truncated logs or user input that was sliced mid-timestamp; feeding ISO week-date strings (which fromisoformat only supports partially and version-dependently) from calendar.ISO calendars; strings produced by string concatenation of date parts with a trailing hyphen.
Related errors
- Invalid isoformat string
- Inconsistent use of dash separator
- Unknown timespec value
- Incomplete time component
- Invalid time separator: %c
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/6d361297cfe1c57b.
Report an issue: GitHub.