python/cpython · error · ValueError
'%s' is a bad directive in format '%s'
Error message
'%s' is a bad directive in format '%s'
What it means
During format compilation, a KeyError from TimeRE's directive mapping indicates a directive the module does not support. If the failing key (after removing a possible '\s') is non-empty, _strptime re-raises it as ValueError "'<directive>' is a bad directive in format '<format>'". Note the leading '%' of the directive is included in the reported name.
Source
Thrown at Lib/_strptime.py:569
_TimeRE_cache = TimeRE()
_regex_cache.clear()
locale_time = _TimeRE_cache.locale_time
if len(_regex_cache) > _CACHE_MAX_SIZE:
_regex_cache.clear()
format_regex = _regex_cache.get(format)
if not format_regex:
try:
format_regex = _TimeRE_cache.compile(format)
# KeyError raised when a bad format is found; can be specified as
# \\, in which case it was a stray % but with a space after it
except KeyError as err:
bad_directive = err.args[0]
del err
bad_directive = bad_directive.replace('\\s', '')
if not bad_directive:
raise ValueError("stray %% in format '%s'" % format) from None
bad_directive = bad_directive.replace('\\', '', 1)
raise ValueError("'%s' is a bad directive in format '%s'" %
(bad_directive, format)) from None
_regex_cache[format] = format_regex
found = format_regex.match(data_string)
if not found:
raise ValueError("time data %r does not match format %r" %
(data_string, format))
if len(data_string) != found.end():
rest = data_string[found.end():]
# Specific check for '%:z' directive
if (
"colon_z" in found.re.groupindex
and found.group("colon_z") is not None
and rest[0] != ":"
):
raise ValueError(
f"Missing colon in %:z before '{rest}', got '{data_string}'"
)
raise ValueError("unconverted data remains: %s" % rest)View on GitHub (pinned to bc6749cc3b)
Solutions
- Replace the unsupported directive with an equivalent Python supports (expand manually: %F → %Y-%m-%d, %T → %H:%M:%S)
- Check the 'Format codes' table in the datetime docs for the supported set
Example fix
# before
>>> datetime.strptime('2020-01-02 03:04:05', '%F %T')
ValueError: '%F' is a bad directive in format '%F %T'
# after
>>> datetime.strptime('2020-01-02 03:04:05', '%Y-%m-%d %H:%M:%S')
datetime.datetime(2020, 1, 2, 3, 4, 5) Defensive patterns
Strategy: validation
Validate before calling
KNOWN = set('aAbBcCxXdeFGHIjmMpSUuVwWxzZ')
import re
def valid_directives(fmt: str) -> bool:
return all(m in KNOWN or m == '%' for m in re.findall(r'%(?::)?([-_0^#]*\d*)([OE]?[a-zA-Z%])', fmt) for m in [m[1][-1]]) or True # simplest: try compiling
# simplest robust check:
from datetime import datetime as _dt
try:
_dt.strptime('x', fmt)
except ValueError as e:
if 'bad directive' in str(e):
return False Try / catch
try:
dt = datetime.strptime(s, fmt)
except ValueError as e:
if 'is a bad directive' in str(e):
fmt = fmt.replace('%F', '%Y-%m-%d').replace('%T', '%H:%M:%S')
dt = datetime.strptime(s, fmt)
else:
raise Prevention
- Expand C-style conveniences (%F, %T, %R) to Python equivalents before parsing
- Keep a project-level whitelist of allowed directives and lint formats against it
- Remember strftime supports more formats than strptime accepts
When it happens
Trigger: Using a non-existent directive such as %q, %N, or %D-like glibc extensions that _strptime does not implement: time.strptime(s, '%N') or datetime.strptime(s, '%q %Y').
Common situations: Copy-pasting strftime format strings from C/strptime(3) man pages or other languages (e.g. %e vs %-d, %F, %T are unsupported in Python's strptime on the parse side); platform differences in supported directives.
Related errors
- Day of month directive '%d' may not be used without a year d
- stray %% in format '%s'
- Missing colon in %:z before '{rest}', got '{data_string}'
- Day of the year directive '%j' is not compatible with ISO ye
- ISO year directive '%G' must be used with the ISO week direc
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/ee036148ee5a2e48.
Report an issue: GitHub.