python/cpython · error · ValueError
Non-digit values in fraction
Error message
Non-digit values in fraction
What it means
Once a '.' or ',' fraction separator is seen, every remaining character must be an ASCII digit. Any letter, sign, or whitespace in the fraction (e.g. '12:30:00.5Z0', '12:30:00.+01:00') raises ValueError('Non-digit values in fraction'). Note this also fires when a timezone suffix was not split off correctly, because _parse_hh_mm_ss_ff receives the naive time portion only.
Source
Thrown at Lib/_pydatetime.py:430
if comp == 0:
has_sep = next_char == ':'
if not next_char or comp >= 2:
break
if has_sep and next_char != ':':
raise ValueError("Invalid time separator: %c" % next_char)
pos += has_sep
if pos < len_str:
if tstr[pos] not in '.,':
raise ValueError("Invalid microsecond separator")
else:
pos += 1
if not all(map(_is_ascii_digit, tstr[pos:])):
raise ValueError("Non-digit values in fraction")
len_remainder = len_str - pos
if len_remainder >= 6:
to_parse = 6
else:
to_parse = len_remainder
time_comps[3] = int(tstr[pos:(pos+to_parse)])
if to_parse < 6:
time_comps[3] *= _FRACTION_CORRECTION[to_parse-1]
return time_comps
def _parse_isoformat_time(tstr):
# Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]
len_str = len(tstr)
if len_str < 2:View on GitHub (pinned to bc6749cc3b)
Solutions
- Uppercase the UTC marker and validate shape: s = s.replace('z', 'Z') then re-check before parsing (on Pythons that lack full ISO support)
- Upgrade to Python 3.11+, where fromisoformat handles most valid ISO-8601 including 'Z'
- Use dateutil.parser.isoparse for permissive but standards-correct ISO input
Example fix
// before
dt = datetime.fromisoformat('2021-01-01T12:30:00.000z') # may hit fraction/garbage errors on <3.11
# after
dt = datetime.fromisoformat('2021-01-01T12:30:00.000Z') # 3.11+
# or: from dateutil.parser import isoparse; dt = isoparse(raw) Defensive patterns
Strategy: validation
Validate before calling
def norm_utc_marker(s: str) -> str:
return s[:-1] + 'Z' if s.endswith('z') else s Type guard
def fraction_is_digits(t: str) -> bool:
import re
m = re.search(r'[.,](\d*)$', t)
return m is None or m.group(1).isdigit() Try / catch
try:
dt = datetime.fromisoformat(s)
except ValueError:
from dateutil.parser import isoparse
dt = isoparse(s) # tolerant fallback for legacy feeds Prevention
- Uppercase 'z' UTC markers before parsing on older Pythons
- Upgrade to Python 3.11+ for full ISO-8601 fromisoformat coverage
- Validate the whole timestamp with one regex before calling fromisoformat
When it happens
Trigger: datetime.fromisoformat('2021-01-01T12:30:00.000+01:00') works, but a malformed sign placement like '12:30:00.5+1:00' can reach the digit check on some paths; fraction fields containing spaces or symbols from bad exports; tz_pos detection missing a non-standard suffix character (e.g. lowercase 'z' in older versions).
Common situations: Lowercase 'z' UTC markers on Python < 3.11 fromisoformat; mixed precision where fraction and offset run together after string surgery; fixed-width record parsing whose offsets drifted.
Related errors
- Invalid microsecond separator
- Invalid ISO string
- Invalid isoformat string
- Inconsistent use of dash separator
- Incomplete time component
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/4b7f176cfa102a9f.
Report an issue: GitHub.