pypa/pip · error · DistlibException

Invalid specification

Error message

Invalid specification '%s'

What it means

Raised as DistlibException by distlib.util.get_export_entry when the specification string fails to match ENTRY_RE (the 'name = module:attr [flags]' export grammar) AND contains an unbalanced or stray '[' or ']'. Because the regex requires well-formed optional flags, a leftover bracket signals a malformed flags section rather than a simply-unknown format. The function returns None (no error) when the spec is unparseable but contains no brackets.

Solutions

  1. Ensure the spec matches 'name = module.path:attribute [flag=value,...]' with balanced brackets.
  2. Remove stray '[' / ']' characters that are not part of a flags block.
  3. Validate entry-point strings with packaging.entry_points or importlib.metadata before passing to get_export_entry.

Example fix

# before
get_export_entry('run = mod:main [console_scripts')
# after
get_export_entry('run = mod:main [console_scripts]')
Defensive patterns

Strategy: validation

Validate before calling

from distlib.util import ENTRY_RE

def valid_export_spec(spec):
    return bool(ENTRY_RE.search(spec)) and spec.count('[') == spec.count(']')

Type guard

def is_balanced_brackets(s: str) -> bool:
    return isinstance(s, str) and s.count('[') == s.count(']')

Try / catch

from distlib.util import DistlibException
try:
    get_export_entry(spec)
except DistlibException as e:
    if 'Invalid specification' in str(e):
        fix_brackets(spec)

Prevention

When it happens

Trigger: get_export_entry('cli = mod:main [bad flag spec'), get_export_entry('name = mod:attr extra['), or any entry-point spec where '[' or ']' appears but the whole string doesn't match the ENTRY_RE grammar (e.g. unbalanced brackets, spaces breaking the regex).

Common situations: Hand-editing entry_points strings in setup.py/setup.cfg; metadata export lists generated by a tool that malformed the flags field; legacy entry point syntax with stray brackets.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/4b4ced204a131d72. Report an issue: GitHub.

Appendix: source

Thrown at src/pip/_vendor/distlib/util.py:729

                      and self.flags == other.flags)
        return result

    __hash__ = object.__hash__


ENTRY_RE = re.compile(
    r'''(?P<name>([^\[]\S*))
                      \s*=\s*(?P<callable>(\w+)([:\.]\w+)*)
                      \s*(\[\s*(?P<flags>[\w-]+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])?
                      ''', re.VERBOSE)


def get_export_entry(specification):
    m = ENTRY_RE.search(specification)
    if not m:
        result = None
        if '[' in specification or ']' in specification:
            raise DistlibException("Invalid specification "
                                   "'%s'" % specification)
    else:
        d = m.groupdict()
        name = d['name']
        path = d['callable']
        colons = path.count(':')
        if colons == 0:
            prefix, suffix = path, None
        else:
            if colons != 1:
                raise DistlibException("Invalid specification "
                                       "'%s'" % specification)
            prefix, suffix = path.split(':')
        flags = d['flags']
        if flags is None:
            if '[' in specification or ']' in specification:
                raise DistlibException("Invalid specification "
                                       "'%s'" % specification)

View on GitHub (pinned to f399c37189)