python/cpython · error · ValueError
Unknown timespec value
Error message
Unknown timespec value
What it means
datetime.isoformat(timespec=...) only accepts 'auto', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds'. The value is used as a key into a format-spec dict; an unknown key raises the bare ValueError('Unknown timespec value') with no echo of the bad input (historically fixed in later CPython versions to include it).
Source
Thrown at Lib/_pydatetime.py:183
def _format_time(hh, mm, ss, us, timespec='auto'):
specs = {
'hours': '{:02d}',
'minutes': '{:02d}:{:02d}',
'seconds': '{:02d}:{:02d}:{:02d}',
'milliseconds': '{:02d}:{:02d}:{:02d}.{:03d}',
'microseconds': '{:02d}:{:02d}:{:02d}.{:06d}'
}
if timespec == 'auto':
# Skip trailing microseconds when us==0.
timespec = 'microseconds' if us else 'seconds'
elif timespec == 'milliseconds':
us //= 1000
try:
fmt = specs[timespec]
except KeyError:
raise ValueError('Unknown timespec value')
else:
return fmt.format(hh, mm, ss, us)
def _format_offset(off, sep=':'):
s = ''
if off is not None:
if off.days < 0:
sign = "-"
off = -off
else:
sign = "+"
hh, mm = divmod(off, timedelta(hours=1))
mm, ss = divmod(mm, timedelta(minutes=1))
s += "%s%02d%s%02d" % (sign, hh, sep, mm)
if ss or ss.microseconds:
s += "%s%02d" % (sep, ss.seconds)
if ss.microseconds:View on GitHub (pinned to bc6749cc3b)
Solutions
- Use one of the six exact strings: 'auto', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds'
- Validate/normalize external input against that set before calling isoformat
- If the value may be absent, pass timespec only when set: dt.isoformat(**({'timespec': ts} if ts else {}))
Example fix
// before
ts = cfg.get('precision', 'ms')
s = dt.isoformat(timespec=ts) # ValueError
# after
_VALID = {'auto','hours','minutes','seconds','milliseconds','microseconds'}
ts = cfg.get('precision', 'auto')
s = dt.isoformat(timespec=ts if ts in _VALID else 'auto') Defensive patterns
Strategy: validation
Validate before calling
_TIMESPECS = {'auto','hours','minutes','seconds','milliseconds','microseconds'}
def safe_isoformat(dt, timespec='auto'):
if timespec not in _TIMESPECS:
raise ValueError(f'timespec must be one of {sorted(_TIMESPECS)}, got {timespec!r}')
return dt.isoformat(timespec=timespec) Type guard
def is_valid_timespec(ts: object) -> bool:
return ts in {'auto','hours','minutes','seconds','milliseconds','microseconds'} Prevention
- Centralize timespec handling behind one validated helper
- Normalize short forms ('ms' -> 'milliseconds') at the config boundary
- Unit-test every user-selectable precision value
When it happens
Trigger: dt.isoformat(timespec='ms') (the timedelta-style abbreviation is invalid here); timespec='secs'; a value read from a config file or env var with a typo; passing None instead of omitting the argument.
Common situations: Confusing datetime.isoformat timespec vocabulary with dateutil/ISO-8601 'millis' strings; user-supplied precision settings ('milli', 'ms', 'M'); config keys propagated from a serialization library that normalizes timespec differently across versions.
Related errors
- Invalid ISO string
- Invalid isoformat string
- Inconsistent use of dash separator
- Incomplete time component
- Invalid time separator: %c
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/da4f176746808b6f.
Report an issue: GitHub.