python/cpython · error · TypeError
bad tzinfo state arg
Error message
bad tzinfo state arg
What it means
Raised by time.__setstate (pickle reconstruction) when the second state element, the tzinfo, is neither None nor a tzinfo instance. The 6-byte base state is unpacked first, then the tzinfo slot is type-checked; passing a string, a timezone name, or an arbitrary object as the tzinfo component fails with this TypeError. It guards against corrupted or hand-built pickle states.
Source
Thrown at Lib/_pydatetime.py:1758
# Pickle support.
def _getstate(self, protocol=3):
us2, us3 = divmod(self._microsecond, 256)
us1, us2 = divmod(us2, 256)
h = self._hour
if self._fold and protocol > 3:
h += 128
basestate = bytes([h, self._minute, self._second,
us1, us2, us3])
if self._tzinfo is None:
return (basestate,)
else:
return (basestate, self._tzinfo)
def __setstate(self, string, tzinfo):
if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):
raise TypeError("bad tzinfo state arg")
h, self._minute, self._second, us1, us2, us3 = string
if h > 127:
self._fold = 1
self._hour = h - 128
else:
self._fold = 0
self._hour = h
self._microsecond = (((us1 << 8) | us2) << 8) | us3
self._tzinfo = tzinfo
def __reduce_ex__(self, protocol):
return (self.__class__, self._getstate(protocol))
def __reduce__(self):
return self.__reduce_ex__(2)
_time_class = time # so functions w/ args named "time" can get at the class
View on GitHub (pinned to bc6749cc3b)
Solutions
- Keep tzinfo objects (or None) in the state; serialize the zone name separately and rebuild via zoneinfo.ZoneInfo(name) after loading
- Remove custom __reduce_ex__ overrides that mangle the state layout
- If subclassing time, delegate pickling to the parent and only add picklable attributes
Example fix
# before
class T(time):
def __reduce__(self):
return (time, (self._getstate()[0], 'UTC')) # str tzinfo -> TypeError on load
# after
from zoneinfo import ZoneInfo
class T(time):
def __reduce__(self):
state, tz = self._getstate()
tz = tz if tz is None else tz # keep real tzinfo object
return (time, (state, tz)) Defensive patterns
Strategy: validation
Validate before calling
from datetime import tzinfo as _tz
def valid_time_state(state):
tz = state[1] if len(state) > 1 else None
return tz is None or isinstance(tz, _tz) Type guard
from datetime import tzinfo as _tz
def is_valid_tz_arg(v) -> bool:
return v is None or isinstance(v, _tz) Try / catch
try:
obj = pickle.loads(data)
except TypeError as e:
if 'bad tzinfo state arg' in str(e):
raise RuntimeError('corrupt or rewritten pickle state for datetime.time') from e
raise Prevention
- Do not override time's pickle methods unless you preserve the exact state layout
- Serialize zone names separately and rebuild tzinfo with zoneinfo on load
- Treat hand-edited pickle payloads as untrusted input; validate state shape before __setstate__
When it happens
Trigger: Constructing time via its pickle form with a bad second argument, e.g. time(b'\x0c\x00\x00\x00\x00\x00', 'UTC'); __setstate__ invoked with a state tuple whose element 1 is a str offset like '+05:00'; custom __reduce_ex__ implementations on subclasses that emit a non-tzinfo object.
Common situations: Hand-crafted or manipulated pickles; subclass overrides of __reduce__/__getstate__ that replace the tzinfo with a serializable surrogate (name string) and forget to restore the object on load; pickle round-trips through frameworks that rewrite state tuples.
Related errors
- cannot compare naive and aware times
- fromisoformat: argument must be str
- tzinfo.tzname() must return None or string, not {type(name).
- tzinfo.{name}() must return None or timedelta, not {type(off
- hour must be in 0..23, not {hour}
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/8db2e31090dca95f.
Report an issue: GitHub.