{"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":"exception","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/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/pkg_resources/__init__.py#L2830-L2866","documentation":"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.","triggerScenarios":"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]'.","commonSituations":"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.","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."],"exampleFix":"# before (entry_points.txt)\nfoo = mod:fn\n[console_scripts]\nbar = mod:bar\n\n# after\n[console_scripts]\nfoo = mod:fn\nbar = mod:bar","handlingStrategy":"validation","validationCode":"def has_header_first(text: str) -> bool:\n    for line in text.splitlines():\n        s = line.strip()\n        if not s or s.startswith('#'):\n            continue\n        return s.startswith('[') and s.endswith(']')\n    return True\nif has_header_first(data):\n    EntryPoint.parse_map(data)","typeGuard":"def is_well_formed_entry_map(text: str) -> bool:\n    # reject if any content line precedes a [section]\n    seen_section = False\n    for line in text.splitlines():\n        s = line.strip()\n        if s.startswith('['):\n            seen_section = True\n        elif s and '=' in s and not seen_section:\n            return False\n    return True","tryCatchPattern":"try:\n    EntryPoint.parse_map(data)\nexcept ValueError as e:\n    if 'must be listed in groups' in str(e):\n        data = '[console_scripts]\\n' + data  # prepend header\n    raise","preventionTips":["Always start entry_points.txt with a [group] header.","Generate entry-point data with configparser/INI tooling."],"tags":["python","pkg-resources","entry-points","metadata"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}