{"record":{"id":"078c91da19e5114d","repo":"pandas-dev/pandas","slug":"k-is-not-a-valid-identifier","errorCode":null,"errorMessage":"{k} is not a valid identifier","messagePattern":"(.+?) is not a valid identifier","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/_config/config.py","lineNumber":574,"sourceCode":"    import tokenize\n\n    key = key.lower()\n\n    if key in _registered_options:\n        raise OptionError(f\"Option '{key}' has already been registered\")\n    if key in _reserved_keys:\n        raise OptionError(f\"Option '{key}' is a reserved key\")\n\n    # the default value should be legal\n    if validator:\n        validator(defval)\n\n    # walk the nested dict, creating dicts as needed along the path\n    path = key.split(\".\")\n\n    for k in path:\n        if not re.match(\"^\" + tokenize.Name + \"$\", k):\n            raise ValueError(f\"{k} is not a valid identifier\")\n        if keyword.iskeyword(k):\n            raise ValueError(f\"{k} is a python keyword\")\n\n    cursor = _global_config\n    msg = \"Path prefix to option '{option}' is already an option\"\n\n    for i, p in enumerate(path[:-1]):\n        if not isinstance(cursor, dict):\n            raise OptionError(msg.format(option=\".\".join(path[:i])))\n        if p not in cursor:\n            cursor[p] = {}\n        cursor = cursor[p]\n\n    if not isinstance(cursor, dict):\n        raise OptionError(msg.format(option=\".\".join(path[:-1])))\n\n    # a namespace already lives here, i.e. `key` is a path prefix to one or\n    # more already-registered options; registering it would clobber them","sourceCodeStart":556,"sourceCodeEnd":592,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/_config/config.py#L556-L592","documentation":"Raised by `register_option` when any dot-separated path component of the key fails the Python-identifier regex `^tokenize.Name$`. Path components must be valid identifiers so the key can be walked as nested attributes/DictWrapper nodes. The offending component `k` is named in the message.","triggerScenarios":"`register_option('a.1bad', ...)`, `register_option('my-lib.flag', ...)` (hyphen), `register_option('a..b', ...)` (empty segment), or any component with spaces/punctuation.","commonSituations":"Using config keys that mirror external identifiers containing dashes, digits-first, or punctuation; pasting keys from JSON/kebab-case config into pandas.","solutions":["Make every dot-separated segment a valid Python identifier (`[A-Za-z_][A-Za-z0-9_]*`).","Replace dashes with underscores; prefix digit-led names with a letter/underscore.","Split the key and validate each segment with `str.isidentifier()` before calling register_option."],"exampleFix":"# before\nregister_option('my-lib.flag', True)   # ValueError: not a valid identifier\n\n# after\nregister_option('my_lib.flag', True)","handlingStrategy":"validation","validationCode":"import re, tokenize\ndef valid_identifier_path(key: str) -> bool:\n    return all(re.match('^' + tokenize.Name + '$', part) for part in key.split('.'))\nif valid_identifier_path(new_key):\n    register_option(new_key, default)","typeGuard":"def is_valid_option_path(key: str) -> bool:\n    import re, tokenize\n    return all(re.match('^' + tokenize.Name + '$', p) for p in key.split('.'))","tryCatchPattern":"try:\n    register_option(key, default)\nexcept ValueError as e:\n    if 'not a valid identifier' in str(e):\n        # sanitize segments (e.g. dash -> underscore) and retry\n        ...\n    raise","preventionTips":["Restrict config keys to `[A-Za-z_][A-Za-z0-9_]*` segments separated by dots.","Convert kebab-case external keys to snake_case before registering.","Validate each segment with `str.isidentifier()` as a fast pre-check."],"tags":["config","register-option","identifier","validation"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}