{"record":{"id":"ee036148ee5a2e48","repo":"python/cpython","slug":"s-is-a-bad-directive-in-format-s","errorCode":null,"errorMessage":"'%s' is a bad directive in format '%s'","messagePattern":"'(.+?)' is a bad directive in format '(.+?)'","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_strptime.py","lineNumber":569,"sourceCode":"            _TimeRE_cache = TimeRE()\n            _regex_cache.clear()\n            locale_time = _TimeRE_cache.locale_time\n        if len(_regex_cache) > _CACHE_MAX_SIZE:\n            _regex_cache.clear()\n        format_regex = _regex_cache.get(format)\n        if not format_regex:\n            try:\n                format_regex = _TimeRE_cache.compile(format)\n            # KeyError raised when a bad format is found; can be specified as\n            # \\\\, in which case it was a stray % but with a space after it\n            except KeyError as err:\n                bad_directive = err.args[0]\n                del err\n                bad_directive = bad_directive.replace('\\\\s', '')\n                if not bad_directive:\n                    raise ValueError(\"stray %% in format '%s'\" % format) from None\n                bad_directive = bad_directive.replace('\\\\', '', 1)\n                raise ValueError(\"'%s' is a bad directive in format '%s'\" %\n                                    (bad_directive, format)) from None\n            _regex_cache[format] = format_regex\n    found = format_regex.match(data_string)\n    if not found:\n        raise ValueError(\"time data %r does not match format %r\" %\n                         (data_string, format))\n    if len(data_string) != found.end():\n        rest = data_string[found.end():]\n        # Specific check for '%:z' directive\n        if (\n            \"colon_z\" in found.re.groupindex\n            and found.group(\"colon_z\") is not None\n            and rest[0] != \":\"\n        ):\n            raise ValueError(\n                f\"Missing colon in %:z before '{rest}', got '{data_string}'\"\n            )\n        raise ValueError(\"unconverted data remains: %s\" % rest)","sourceCodeStart":551,"sourceCodeEnd":587,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_strptime.py#L551-L587","documentation":"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.","triggerScenarios":"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').","commonSituations":"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.","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"],"exampleFix":"# before\n>>> datetime.strptime('2020-01-02 03:04:05', '%F %T')\nValueError: '%F' is a bad directive in format '%F %T'\n\n# after\n>>> datetime.strptime('2020-01-02 03:04:05', '%Y-%m-%d %H:%M:%S')\ndatetime.datetime(2020, 1, 2, 3, 4, 5)","handlingStrategy":"validation","validationCode":"KNOWN = set('aAbBcCxXdeFGHIjmMpSUuVwWxzZ')\nimport re\n\ndef valid_directives(fmt: str) -> bool:\n    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\n\n# simplest robust check:\nfrom datetime import datetime as _dt\ntry:\n    _dt.strptime('x', fmt)\nexcept ValueError as e:\n    if 'bad directive' in str(e):\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    dt = datetime.strptime(s, fmt)\nexcept ValueError as e:\n    if 'is a bad directive' in str(e):\n        fmt = fmt.replace('%F', '%Y-%m-%d').replace('%T', '%H:%M:%S')\n        dt = datetime.strptime(s, fmt)\n    else:\n        raise","preventionTips":["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"],"tags":["strptime","format-string","directive","cpython"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}