RustPython/RustPython · error · ValueError

Invalid ISO string

Error message

Invalid ISO string

What it means

_find_isoformat_datetime_separator is the internal helper that decides where the date portion of a fromisoformat string ends. In the ISO week-date branch (dtstr[5] == 'W'), a string too short to still contain the two-digit week number (len_dtstr < 8) is rejected with ValueError('Invalid ISO string'). The branch is largely shielded by the len == 7 early return and the following assert, so it mainly bites degenerate inputs like '2019-W', especially under python -O where asserts are stripped.

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 aaeab4f754)

Solutions

  1. Emit the full week date: '2019-W01' (len 8) or '2019W01'
  2. Validate the shape with a regex like ^\d{4}-?W\d{2}$ before calling fromisoformat
  3. Wrap fromisoformat in try/except ValueError and reject the field at the input boundary

Example fix

# before
date.fromisoformat('2019-W')  # Invalid ISO string

# after
date.fromisoformat('2019-W01')  # datetime.date(2019, 1, 7)
Defensive patterns

Strategy: try-catch

Validate before calling

import re
ISO_WEEK_DATE = re.compile(r'^\d{4}-?W\d{2}(-?\d)?$')
if not ISO_WEEK_DATE.match(s):
    raise ValueError(f'not a valid ISO week date: {s!r}')
d = date.fromisoformat(s)

Try / catch

try:
    d = date.fromisoformat(s)
except (ValueError, AssertionError):
    raise ValueError(f'malformed ISO date: {s!r}') from None

Prevention

When it happens

Trigger: date.fromisoformat('2019-W') or datetime.fromisoformat('2019-W') — a week indicator with no room for the mandatory two-digit week number. Without -O this typically surfaces as an AssertionError from the guard instead.

Common situations: Truncated date fields from CSV/log ingestion; hand-assembled ISO week strings; tests feeding partial strings to check parser behavior.

Related errors


AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17). Data as JSON: /api/errors/c3606f2ebc40885f. Report an issue: GitHub.