pandas-dev/pandas · error · ValueError

is not a valid identifier

Error message

{k} is not a valid identifier

What it means

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.

Solutions

  1. Make every dot-separated segment a valid Python identifier (`[A-Za-z_][A-Za-z0-9_]*`).
  2. Replace dashes with underscores; prefix digit-led names with a letter/underscore.
  3. Split the key and validate each segment with `str.isidentifier()` before calling register_option.

Example fix

# before
register_option('my-lib.flag', True)   # ValueError: not a valid identifier

# after
register_option('my_lib.flag', True)
Defensive patterns

Strategy: validation

Validate before calling

import re, tokenize
def valid_identifier_path(key: str) -> bool:
    return all(re.match('^' + tokenize.Name + '$', part) for part in key.split('.'))
if valid_identifier_path(new_key):
    register_option(new_key, default)

Type guard

def is_valid_option_path(key: str) -> bool:
    import re, tokenize
    return all(re.match('^' + tokenize.Name + '$', p) for p in key.split('.'))

Try / catch

try:
    register_option(key, default)
except ValueError as e:
    if 'not a valid identifier' in str(e):
        # sanitize segments (e.g. dash -> underscore) and retry
        ...
    raise

Prevention

When it happens

Trigger: `register_option('a.1bad', ...)`, `register_option('my-lib.flag', ...)` (hyphen), `register_option('a..b', ...)` (empty segment), or any component with spaces/punctuation.

Common situations: Using config keys that mirror external identifiers containing dashes, digits-first, or punctuation; pasting keys from JSON/kebab-case config into pandas.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/078c91da19e5114d. Report an issue: GitHub.

Appendix: source

Thrown at pandas/_config/config.py:574

    import tokenize

    key = key.lower()

    if key in _registered_options:
        raise OptionError(f"Option '{key}' has already been registered")
    if key in _reserved_keys:
        raise OptionError(f"Option '{key}' is a reserved key")

    # the default value should be legal
    if validator:
        validator(defval)

    # walk the nested dict, creating dicts as needed along the path
    path = key.split(".")

    for k in path:
        if not re.match("^" + tokenize.Name + "$", k):
            raise ValueError(f"{k} is not a valid identifier")
        if keyword.iskeyword(k):
            raise ValueError(f"{k} is a python keyword")

    cursor = _global_config
    msg = "Path prefix to option '{option}' is already an option"

    for i, p in enumerate(path[:-1]):
        if not isinstance(cursor, dict):
            raise OptionError(msg.format(option=".".join(path[:i])))
        if p not in cursor:
            cursor[p] = {}
        cursor = cursor[p]

    if not isinstance(cursor, dict):
        raise OptionError(msg.format(option=".".join(path[:-1])))

    # a namespace already lives here, i.e. `key` is a path prefix to one or
    # more already-registered options; registering it would clobber them

View on GitHub (pinned to 3b7651241d)