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 8

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. 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
  2. Strip trailing separators: s.rstrip('-') then re-check format
  3. 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

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


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