python/cpython · error · ValueError

Failed to encode latin1 string when unpickling a datetime ob

Error message

Failed to encode latin1 string when unpickling a datetime object. pickle.load(data, encoding='latin1') is assumed.

What it means

Raised by datetime.__new__ during unpickling when the 10-character str payload cannot be encoded to latin1. Datetimes are pickled as a compact byte string; loading such a pickle with a non-latin1 encoding (e.g. encoding='utf-8') can yield a str with characters above U+00FF, and reconstruction aborts with this ValueError telling you the assumed encoding.

Source

Thrown at Lib/_pydatetime.py:1800

class datetime(date):
    """A combination of a date and a time.

    The year, month and day arguments are required. tzinfo may be None, or an
    instance of a tzinfo subclass. The remaining arguments may be ints.
    """
    __slots__ = time.__slots__

    def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
                microsecond=0, tzinfo=None, *, fold=0):
        if (isinstance(year, (bytes, str)) and len(year) == 10 and
            1 <= ord(year[2:3])&0x7F <= 12):
            # Pickle support
            if isinstance(year, str):
                try:
                    year = bytes(year, 'latin1')
                except UnicodeEncodeError:
                    # More informative error message.
                    raise ValueError(
                        "Failed to encode latin1 string when unpickling "
                        "a datetime object. "
                        "pickle.load(data, encoding='latin1') is assumed.")
            self = object.__new__(cls)
            self.__setstate(year, month)
            self._hashcode = -1
            return self
        year, month, day = _check_date_fields(year, month, day)
        hour, minute, second, microsecond, fold = _check_time_fields(
            hour, minute, second, microsecond, fold)
        _check_tzinfo_arg(tzinfo)
        self = object.__new__(cls)
        self._year = year
        self._month = month
        self._day = day
        self._hour = hour
        self._minute = minute
        self._second = second

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Load with encoding='latin1': pickle.load(f, encoding='latin1')
  2. Re-pickle everything in Python 3 (protocol 4+) or migrate storage to ISO-format JSON
  3. Write a one-time conversion script that loads with latin1 and re-dumps safely

Example fix

# before
obj = pickle.load(f, encoding='utf-8')

# after
obj = pickle.load(f, encoding='latin1')
Defensive patterns

Strategy: try-catch

Validate before calling

if isinstance(v, str) and len(v) == 10 and any(ord(c) > 255 for c in v[:3]):
    raise ValueError('non-latin1 datetime payload; use encoding=latin1')

Try / catch

try:
    obj = pickle.load(f, encoding='latin1')
except ValueError:
    # not a latin1-encoded pickle after all; surface clearly
    raise

Prevention

When it happens

Trigger: pickle.load(f, encoding='utf-8') on a pickle containing a datetime.datetime; Python 2 pickles of datetimes loaded into Python 3 with the wrong encoding argument; pickle tools/streams that decode payload strings to utf-8 by default.

Common situations: Python 2 -> 3 data migrations of pickled records; cached pickles in files/Redis/DBs written by old interpreters; generic unpickle helpers defaulting encoding to 'utf-8'.

Related errors


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