pypa/pip · error · ValueError

Invalid module name

Error message

Invalid module name

What it means

Raised as ValueError by EntryPoint.__init__ when module_name fails the MODULE regex (`\w+(\.\w+)*$`). Module names must consist of word characters (letters, digits, underscore) and dotted segments; any other character (hyphen, slash, space, leading dot) is rejected. Note the error message is exactly 'Invalid module name' with the offending value as the second argument.

Source

Thrown at src/pip/_vendor/pkg_resources/__init__.py:2693

    )?
    """,
    re.VERBOSE | re.IGNORECASE,
).match


class EntryPoint:
    """Object representing an advertised importable object"""

    def __init__(
        self,
        name: str,
        module_name: str,
        attrs: Iterable[str] = (),
        extras: Iterable[str] = (),
        dist: Distribution | None = None,
    ):
        if not MODULE(module_name):
            raise ValueError("Invalid module name", module_name)
        self.name = name
        self.module_name = module_name
        self.attrs = tuple(attrs)
        self.extras = tuple(extras)
        self.dist = dist

    def __str__(self):
        s = "%s = %s" % (self.name, self.module_name)
        if self.attrs:
            s += ':' + '.'.join(self.attrs)
        if self.extras:
            s += ' [%s]' % ','.join(self.extras)
        return s

    def __repr__(self):
        return "EntryPoint.parse(%r)" % str(self)

    @overload

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Normalize the module name before constructing the EntryPoint: replace '-' with '_' (and any other non-word char) to match Python's import identifier rules.
  2. Validate with the same regex first: `if MODULE(module_name): EntryPoint(...)` (MODULE = re.compile(r'\w+(\.\w+)*$').match).
  3. Build EntryPoints via EntryPoint.parse() from a well-formed 'name=module:attr' string, which applies the same validation through its pattern.

Example fix

// before
ep = EntryPoint('cli', 'my-pkg.cli', ['main'])  # ValueError: Invalid module name

// after
module_name = 'my-pkg.cli'.replace('-', '_')
ep = EntryPoint('cli', module_name, ['main'])  # 'my_pkg.cli' — OK
Defensive patterns

Strategy: validation

Validate before calling

import re
MODULE = re.compile(r'\w+(\.\w+)*$').match

def safe_entry_point(name, module_name, attrs=()):
    if not MODULE(module_name):
        # normalize hyphens and other common project-name chars
        module_name = re.sub(r'[^\w.]', '_', module_name)
        if not MODULE(module_name):
            raise ValueError(f'Invalid module name: {module_name!r}')
    from pkg_resources import EntryPoint
    return EntryPoint(name, module_name, attrs)

Type guard

import re
MODULE = re.compile(r'\w+(\.\w+)*$').match

def is_valid_module_name(name: str) -> bool:
    return isinstance(name, str) and bool(MODULE(name))

Try / catch

try:
    ep = EntryPoint(name, module_name, attrs)
except ValueError as e:
    if 'Invalid module name' in str(e):
        module_name = module_name.replace('-', '_')
        ep = EntryPoint(name, module_name, attrs)
    else:
        raise

Prevention

When it happens

Trigger: Constructing an EntryPoint(name, module_name, ...) directly with a module_name containing invalid characters — most commonly a hyphenated project name used verbatim as a module ('my-pkg.app') instead of the underscored import name ('my_pkg.app').

Common situations: Auto-generating entry points from PyPI project names (which allow hyphens) without normalizing to the import name; typos in entry_points.txt; programmatic EntryPoint creation from untrusted/user input.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/439b81e94083b47a.json. Report an issue: GitHub.