python/cpython · error · ValueError

unconverted data remains: %s

Error message

unconverted data remains: %s

What it means

The format regex matched a prefix of the input but data_string is longer than what the format covers — len(data_string) != found.end() with no %:z colon case. The ValueError reports the leftover suffix (rest = data_string[found.end():]).

Source

Thrown at Lib/_strptime.py:587

                raise ValueError("'%s' is a bad directive in format '%s'" %
                                    (bad_directive, format)) from None
            _regex_cache[format] = format_regex
    found = format_regex.match(data_string)
    if not found:
        raise ValueError("time data %r does not match format %r" %
                         (data_string, format))
    if len(data_string) != found.end():
        rest = data_string[found.end():]
        # Specific check for '%:z' directive
        if (
            "colon_z" in found.re.groupindex
            and found.group("colon_z") is not None
            and rest[0] != ":"
        ):
            raise ValueError(
                f"Missing colon in %:z before '{rest}', got '{data_string}'"
            )
        raise ValueError("unconverted data remains: %s" % rest)

    iso_year = year = None
    month = day = 1
    hour = minute = second = fraction = 0
    tz = -1
    gmtoff = None
    gmtoff_fraction = 0
    iso_week = week_of_year = None
    week_of_year_start = None
    # weekday and julian defaulted to None so as to signal need to calculate
    # values
    weekday = julian = None
    found_dict = found.groupdict()
    if locale_time.LC_alt_digits:
        def parse_int(s):
            try:
                return locale_time.LC_alt_digits.index(s)
            except ValueError:

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Add directives for the remaining text (%H:%M:%S, %f, %Z, %z) or matching literals
  2. Strip/slice the input to exactly what the format covers: data.split(' ')[0]
  3. Use dateutil.parser or fromisoformat for variable-length inputs

Example fix

# before
>>> datetime.strptime('2020-01-01 12:00', '%Y-%m-%d')
ValueError: unconverted data remains:  12:00

# after
>>> datetime.strptime('2020-01-01 12:00', '%Y-%m-%d %H:%M')
datetime.datetime(2020, 1, 1, 12, 0)
Defensive patterns

Strategy: fallback

Validate before calling

from datetime import datetime

def parse_prefix(s: str, fmt: str):
    for end in range(len(s), 0, -1):
        try:
            return datetime.strptime(s[:end], fmt)
        except ValueError:
            continue
    raise ValueError(f'no prefix of {s!r} matches {fmt!r}')

Try / catch

try:
    dt = datetime.strptime(line.strip(), fmt)
except ValueError as e:
    if str(e).startswith('unconverted data remains'):
        dt = datetime.strptime(line.strip()[:len_covered(fmt)], fmt)
    else:
        raise

Prevention

When it happens

Trigger: datetime.strptime('2020-01-01 12:00', '%Y-%m-%d') — the time part ' 12:00' remains; or a trailing timezone name, milliseconds, or newline not covered by the format.

Common situations: Log lines with extra trailing fields; input with trailing '\n' from file.readline(); forgetting %H:%M:%S or %f/%Z directives for the tail of the string.

Related errors


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