pypa/pip · error · ValueError

Cannot serialize {obj!r} where tzinfo=None

Error message

Cannot serialize {obj!r} where tzinfo=None

What it means

Raised by msgpack's pure-Python Packer._pack when datetime support is enabled (datetime=True, the default in newer versions) but the datetime object has tzinfo=None — a naive datetime. The Timestamp extension type computes UTC seconds from dt.timestamp(), which for naive datetimes is ambiguous (interpreted as local time), so msgpack explicitly refuses to serialize it to prevent silent data corruption.

Source

Thrown at src/pip/_vendor/msgpack/fallback.py:802

                self._pack_array_header(n)
                for i in range(n):
                    self._pack(obj[i], nest_limit - 1)
                return
            if check(obj, dict):
                return self._pack_map_pairs(len(obj), obj.items(), nest_limit - 1)

            if self._datetime and check(obj, _DateTime) and obj.tzinfo is not None:
                obj = Timestamp.from_datetime(obj)
                default_used = 1
                continue

            if not default_used and self._default is not None:
                obj = self._default(obj)
                default_used = 1
                continue

            if self._datetime and check(obj, _DateTime):
                raise ValueError(f"Cannot serialize {obj!r} where tzinfo=None")

            raise TypeError(f"Cannot serialize {obj!r}")

    def pack(self, obj):
        try:
            self._pack(obj)
        except:
            self._buffer = BytesIO()  # force reset
            raise
        if self._autoreset:
            ret = self._buffer.getvalue()
            self._buffer = BytesIO()
            return ret

    def pack_map_pairs(self, pairs):
        self._pack_map_pairs(len(pairs), pairs)
        if self._autoreset:
            ret = self._buffer.getvalue()

View on GitHub (pinned to f399c37189)

Solutions

  1. Make the datetime timezone-aware before packing: dt.replace(tzinfo=timezone.utc) or dt.astimezone(timezone.utc).
  2. Use datetime.now(timezone.utc) instead of datetime.now() at creation time.
  3. If the original timezone is unknown but you know it is UTC, attach tzinfo=datetime.timezone.utc explicitly.
  4. Disable datetime support (datetime=False) and serialize the datetime yourself if you must handle naive datetimes.

Example fix

# before
import datetime
packer = msgpack.Packer(datetime=True)
packer.pack(datetime.datetime(2024, 1, 1))  # naive — raises

# after
dt = datetime.datetime(2024, 1, 1, tzinfo=datetime.timezone.utc)
packer.pack(dt)
Defensive patterns

Strategy: validation

Validate before calling

import datetime

def is_aware_datetime(dt) -> bool:
    return isinstance(dt, datetime.datetime) and dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None

Type guard

import datetime

def is_timezone_aware(dt) -> bool:
    return isinstance(dt, datetime.datetime) and dt.tzinfo is not None

Try / catch

import datetime
for dt in datetimes:
    if dt.tzinfo is None:
        dt = dt.replace(tzinfo=datetime.timezone.utc)
    packer.pack(dt)

Prevention

When it happens

Trigger: Packing a datetime.datetime or datetime.time without tzinfo set while the Packer is configured with datetime=True (or the default enables it). The code path at line 791 checks obj.tzinfo is not None before converting; line 802 catches the fallback where tzinfo is still None.

Common situations: Application creates datetime.now() (naive, local) instead of datetime.now(timezone.utc); data from a database or CSV parsed as naive datetimes; legacy code predating timezone-aware datetimes.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/680d5c3272457a26. Report an issue: GitHub.