pypa/pip · error · ValueError

Duplicate group name

Error message

Duplicate group name

What it means

Raised by EntryPoint.parse_map when the same '[group]' section header appears more than once in the parsed data. Because the result is a dict keyed by group, a repeated header is ambiguous and pkg_resources rejects it with ValueError('Duplicate group name', group).

Source

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

        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), '')
    _, _, value = line.partition(':')
    return safe_version(value.strip()) or None

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Merge the entries of the duplicated group under a single '[group]' header.
  2. Regenerate the metadata from a single consolidated entry_points/ pyproject.toml source.
  3. If merging programmatically, accumulate lines per group into a dict before serializing instead of concatenating raw sections.

Example fix

# before
[console_scripts]
foo = a:main
[console_scripts]
bar = b:main

# after
[console_scripts]
foo = a:main
bar = b:main
Defensive patterns

Strategy: validation

Validate before calling

from configparser import ConfigParser
cp = ConfigParser()
cp.read_string(entry_points_txt)
# ConfigParser merges duplicate sections silently; to detect manually:
headers = [ln.strip() for ln in entry_points_txt.splitlines() if ln.strip().startswith('[')]
assert len(headers) == len(set(headers)), 'duplicate group section'

Type guard

def has_unique_groups(text: str) -> bool:
    seen = set()
    for line in text.splitlines():
        s = line.strip()
        if s.startswith('[') and s.endswith(']'):
            if s in seen:
                return False
            seen.add(s)
    return True

Try / catch

try:
    EntryPoint.parse_map(data)
except ValueError as e:
    if 'Duplicate group name' in str(e):
        # merge sections, then retry
        ...
    raise

Prevention

When it happens

Trigger: entry_points.txt (or a dict passed to parse_map) contains two identical section headers, e.g. two '[console_scripts]' blocks; parse_map encounters the group already in 'maps'.

Common situations: Concatenating metadata from multiple sources, a packaging tool that appends a group instead of merging into the existing one, or manual editing that duplicated a header.

Related errors


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