pypa/pip · error · ValueError
Entry points must be listed in groups
Error message
Entry points must be listed in groups
What it means
Raised by EntryPoint.parse_map when entry-point definition lines appear before any '[group]' section header. parse_map uses split_sections; if it yields a (None, non-empty-lines) tuple it means content exists with no group, which is meaningless for entry points and is rejected with ValueError.
Source
Thrown at src/pip/_vendor/pkg_resources/__init__.py:2848
@classmethod
def parse_map(
cls,
data: str | Iterable[str] | dict[str, str | Iterable[str]],
dist: Distribution | None = None,
):
"""Parse a map of entry point groups"""
_data: Iterable[tuple[str | None, str | Iterable[str]]]
if isinstance(data, dict):
_data = data.items()
else:
_data = split_sections(data)
maps: dict[str, dict[str, Self]] = {}
for group, lines in _data:
if group is None:
if not lines:
continue
raise ValueError("Entry points must be listed in groups")
group = group.strip()
if group in maps:
raise ValueError("Duplicate group name", group)
maps[group] = cls.parse_group(group, lines, dist)
return maps
def _version_from_file(lines):
"""
Given an iterable of lines from a Metadata file, return
the value of the Version field, if present, or None otherwise.
"""
def is_version_line(line):
return line.lower().startswith('version:')
version_lines = filter(is_version_line, lines)
line = next(iter(version_lines), '')View on GitHub (pinned to d7d0d0a394)
Solutions
- Ensure every entry-point line in the data is preceded by a '[group_name]' section header.
- If building the map from a dict, pass {group: [lines]} so each key is a group name rather than passing bare lines.
- Re-emit the file via your build backend (setuptools/hatch) to get well-formed sections.
Example fix
# before (entry_points.txt) foo = mod:fn [console_scripts] bar = mod:bar # after [console_scripts] foo = mod:fn bar = mod:bar
Defensive patterns
Strategy: validation
Validate before calling
def has_header_first(text: str) -> bool:
for line in text.splitlines():
s = line.strip()
if not s or s.startswith('#'):
continue
return s.startswith('[') and s.endswith(']')
return True
if has_header_first(data):
EntryPoint.parse_map(data) Type guard
def is_well_formed_entry_map(text: str) -> bool:
# reject if any content line precedes a [section]
seen_section = False
for line in text.splitlines():
s = line.strip()
if s.startswith('['):
seen_section = True
elif s and '=' in s and not seen_section:
return False
return True Try / catch
try:
EntryPoint.parse_map(data)
except ValueError as e:
if 'must be listed in groups' in str(e):
data = '[console_scripts]\n' + data # prepend header
raise Prevention
- Always start entry_points.txt with a [group] header.
- Generate entry-point data with configparser/INI tooling.
When it happens
Trigger: Calling EntryPoint.parse_map on a string whose first non-blank lines are entry definitions rather than a section header, e.g. 'foo = mod:fn\n[console_scripts]'.
Common situations: A truncated or hand-written entry_points.txt missing its initial section header, or code that joins entry-point lines without a leading '[group]' line.
Related errors
- Invalid group name
- Duplicate entry point
- Duplicate group name
- Missing 'Version:' header and/or {} file at path: {}
- Entry point %r not found
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/c4babf714461d33a.json.
Report an issue: GitHub.