{"record":{"id":"c4babf714461d33a","repo":"pypa/pip","slug":"entry-points-must-be-listed-in-groups","errorCode":null,"errorMessage":"Entry points must be listed in groups","messagePattern":"Entry points must be listed in groups","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/pkg_resources/__init__.py","lineNumber":2848,"sourceCode":"\n    @classmethod\n    def parse_map(\n        cls,\n        data: str | Iterable[str] | dict[str, str | Iterable[str]],\n        dist: Distribution | None = None,\n    ):\n        \"\"\"Parse a map of entry point groups\"\"\"\n        _data: Iterable[tuple[str | None, str | Iterable[str]]]\n        if isinstance(data, dict):\n            _data = data.items()\n        else:\n            _data = split_sections(data)\n        maps: dict[str, dict[str, Self]] = {}\n        for group, lines in _data:\n            if group is None:\n                if not lines:\n                    continue\n                raise ValueError(\"Entry points must be listed in groups\")\n            group = group.strip()\n            if group in maps:\n                raise ValueError(\"Duplicate group name\", group)\n            maps[group] = cls.parse_group(group, lines, dist)\n        return maps\n\n\ndef _version_from_file(lines):\n    \"\"\"\n    Given an iterable of lines from a Metadata file, return\n    the value of the Version field, if present, or None otherwise.\n    \"\"\"\n\n    def is_version_line(line):\n        return line.lower().startswith('version:')\n\n    version_lines = filter(is_version_line, lines)\n    line = next(iter(version_lines), '')","sourceCodeStart":2830,"sourceCodeEnd":2866,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/pkg_resources/__init__.py#L2830-L2866","documentation":"Raised as ValueError in EntryPoint.parse_map at line 2848 when entry point lines appear before any group section header. The split_sections function returns (None, content) for lines before the first '[group]'; if content exists under section=None, it means orphaned lines with no group context.","triggerScenarios":"An entry_points.txt file that has lines before the first '[group]' section header, or a multi-line string passed to parse_map without a leading section. The check `if group is None` with non-empty lines triggers the error.","commonSituations":"Truncated or hand-edited entry_points.txt missing the section header; copy-paste that drops the '[console_scripts]' line; metadata generation tools that emit raw lines first.","solutions":["Ensure every entry point line is preceded by a '[group_name]' section header in entry_points.txt.","Add the missing section header at the top of the file.","Regenerate the .egg-info/entry_points.txt by reinstalling or running setup.py egg_info.","Validate metadata files before packaging."],"exampleFix":"// before: entry_points.txt\n// mycli = mypkg:main\n// [console_scripts]\n// other = otherpkg:run\n\n// after\n// [console_scripts]\n// mycli = mypkg:main\n// other = otherpkg:run","handlingStrategy":"validation","validationCode":"def ensure_group_header(text: str) -> str:\n    lines = text.strip().splitlines()\n    if lines and not lines[0].strip().startswith('['):\n        lines.insert(0, '[console_scripts]')\n    return '\\n'.join(lines)","typeGuard":"def all_lines_under_sections(text: str) -> bool:\n    in_section = False\n    for line in text.strip().splitlines():\n        stripped = line.strip()\n        if stripped.startswith('['):\n            in_section = True\n        elif stripped and '=' in stripped and not in_section:\n            return False\n    return True\n","tryCatchPattern":"from pkg_resources import EntryPoint\ntry:\n    ep_map = EntryPoint.parse_map(raw_text)\nexcept ValueError as e:\n    if 'must be listed in groups' in str(e):\n        ep_map = EntryPoint.parse_map(ensure_group_header(raw_text))\n    else:\n        raise\n","preventionTips":["Ensure entry_points.txt always starts with a '[group]' section header.","Validate metadata files before packaging.","Regenerate entry_points.txt via reinstall if hand-edited."],"tags":["entry-points","pkg-resources","metadata","section-header"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}