{"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/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/_config/config.py#L556-L592","documentation":"Raised by register_option (config.py:572-574) as a ValueError when any path segment of the dotted key does not match the regex `^tokenize.Name$` (a Python identifier). Each component of the dotted key must be a legal identifier on its own.","triggerScenarios":"cf.register_option('display.1col', 1) — '1col' starts with a digit; or 'display.foo-bar' where 'foo-bar' contains a hyphen.","commonSituations":"Embedding user input or numeric values into an option key; using kebab-case instead of dot.snake_case.","solutions":["Use only identifier-safe characters: letters, digits (not leading), underscores within each segment.","Sanitize each segment with re.sub(r'\\W', '_', seg) and ensure it does not start with a digit.","Validate with `seg.isidentifier()` per segment before calling register_option."],"exampleFix":"# before\ncf.register_option('display.1col', 1)  # 1col is not a valid identifier\n\n# after\ncf.register_option('display.first_col', 1)","handlingStrategy":"validation","validationCode":"import re, tokenize\ndef valid_segments(key: str) -> bool:\n    return all(re.match('^' + tokenize.Name + '$', seg) for seg in key.lower().split('.'))","typeGuard":"def is_valid_option_key(key: str) -> bool:\n    import re, tokenize\n    return all(re.match('^' + tokenize.Name + '$', s) for s in key.split('.'))","tryCatchPattern":null,"preventionTips":["Use only Python-identifier segments (snake_case) in option keys.","Sanitize dynamic segments with re.sub(r'\\W+', '_', s) and prefix a letter if it starts with a digit.","Add a unit test asserting key validity before registration."],"tags":["config","register-option","identifier","validation"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}