python/cpython · error · ValueError

Invalid isoformat string: {time_string!r}

Error message

Invalid isoformat string: {time_string!r}

What it means

Raised by time.fromisoformat() when _parse_isoformat_time cannot parse the string as an ISO 8601 time. The original lower-level ValueError is suppressed (`from None`) and replaced with this message echoing the offending string, so the repr of the bad input is visible in the traceback. Common causes are stray characters, wrong separators, or invalid component values.

Source

Thrown at Lib/_pydatetime.py:1650

    __str__ = isoformat

    @classmethod
    def fromisoformat(cls, time_string):
        """Construct a time from a string in one of the ISO 8601 formats."""
        if not isinstance(time_string, str):
            raise TypeError('fromisoformat: argument must be str')

        # The spec actually requires that time-only ISO 8601 strings start with
        # T, but the extended format allows this to be omitted as long as there
        # is no ambiguity with date strings.
        time_string = time_string.removeprefix('T')

        try:
            time_components, _, error_from_components, error_from_tz = (
                _parse_isoformat_time(time_string)
            )
        except ValueError:
            raise ValueError(
                f'Invalid isoformat string: {time_string!r}') from None
        else:
            if error_from_tz:
                raise error_from_tz
            if error_from_components:
                raise ValueError(
                    "Minute, second, and microsecond must be 0 when hour is 24"
                )

            return cls(*time_components)

    def strftime(self, format):
        """Format using strftime().  The date part of the timestamp passed
        to underlying strftime should not be used.

        For a list of supported format codes, see the documentation:
            https://docs.python.org/3/library/datetime.html#format-codes
        """

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Normalize the input before parsing: strip whitespace, convert 12-hour AM/PM to 24-hour, replace '.' time separators with ':'
  2. Use dateutil.parser.parse or a targeted strptime format for non-ISO inputs: datetime.strptime(s, '%I:%M %p').time()
  3. For free-form fields, validate with a regex (e.g. ^\d{2}:\d{2}(:\d{2}(\.\d+)?)?$) before calling fromisoformat

Example fix

# before
t = time.fromisoformat('1:30 PM')

# after
from datetime import datetime
t = datetime.strptime('1:30 PM', '%I:%M %p').time()
Defensive patterns

Strategy: validation

Validate before calling

import re
ISO_TIME = re.compile(r'^\d{2}:\d{2}(:\d{2}(\.\d{1,6})?)?(Z|[+-]\d{2}:?\d{2})?$')
s = s.strip()
if not ISO_TIME.match(s):
    raise ValueError(f'not an ISO time: {s!r}')
t = time.fromisoformat(s)

Type guard

def looks_like_iso_time(s: str) -> bool:
    return bool(re.match(r'^\d{2}:\d{2}(:\d{2}(\.\d{1,6})?)?$', s))

Try / catch

try:
    t = time.fromisoformat(s)
except ValueError:
    t = datetime.strptime(s.strip(), '%I:%M %p').time()  # known fallback format

Prevention

When it happens

Trigger: time.fromisoformat('12.30.00') (dots instead of colons); time.fromisoformat('25:00') (hour out of range); time.fromisoformat('12:30:00 UTC') (trailing junk); time.fromisoformat('2024-01-01') (a date, not a time); time.fromisoformat('') (empty string).

Common situations: User-supplied 'HH:MM' input forms (these are valid), but variants like '12 noon', 'h12:30', or locale-formatted times are not; concatenating date and time without the separator; whitespace not stripped; CSV/Excel exports with non-ISO times like '1:30 PM'.

Related errors


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