{"record":{"id":"e974a5d4898040a6","repo":"pypa/pip","slug":"tag-component-component-r-is-not-in-sorted-order","errorCode":null,"errorMessage":"Tag component {component!r} is not in sorted order per PEP 425","messagePattern":"Tag component (.+?) is not in sorted order per PEP 425","errorType":"validation","errorClass":"UnsortedTagsError","httpStatus":null,"severity":"warning","filePath":"src/pip/_vendor/packaging/tags.py","lineNumber":284,"sourceCode":"    .. versionadded:: 26.3\n       Raises :class:`InvalidTag` when an interpreter component is not an\n       identifier, a tag component is empty, or a tag does not have exactly\n       three components.\n       Added the *limit* parameter. Raises :class:`TooManyTagsError` if the compressed\n       tag set would generate more than *limit* tags.\n    \"\"\"\n\n    if limit is not None and limit < 0:\n        raise ValueError(\"limit must be non-negative\")\n\n    component_parts = [component.split(\".\") for component in tag.split(\"-\")]\n    for parts in component_parts:\n        if \"\" in parts:\n            component = \".\".join(parts)\n            raise InvalidTag(f\"Tag {tag!r} has an empty component: {component!r}\")\n        if validate_order and parts != sorted(parts):\n            component = \".\".join(parts)\n            raise UnsortedTagsError(\n                f\"Tag component {component!r} is not in sorted order per PEP 425\"\n            )\n\n    tag_count = 1\n    for parts in component_parts:\n        tag_count *= len(parts)\n\n    if limit is not None and tag_count > limit:\n        raise TooManyTagsError(\n            f\"Compressed tag set would generate {tag_count} tags, exceeding \"\n            f\"limit {limit}\"\n        )\n\n    try:\n        interpreters, abis, platforms = component_parts\n    except ValueError as exc:\n        raise InvalidTag(f\"Tag {tag!r} must have exactly three components\") from exc\n    for interpreter in interpreters:","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/tags.py#L266-L302","documentation":"Raised as UnsortedTagsError (ValueError subclass) by parse_tag (tags.py:282-286) only when validate_order=True and a compressed component's '.'-separated parts are not in ascending sorted order. PEP 425 requires compressed tag components to be sorted so index lookups can short-circuit; this check enforces it. The comparison is lexicographic string sort (parts != sorted(parts)).","triggerScenarios":"parse_tag('py3.py2-none-any', validate_order=True) (['py3','py2'] != sorted ['py2','py3']); parse_tag('cp310.cp39-...', validate_order=True); any compressed tag where a higher version precedes a lower one within a component.","commonSituations":"A wheel index/mirror tool enforcing PEP 425 ordering on submitted compressed tags; a build tool generating py2.py3 tags in the wrong order; validating user-uploaded wheel filenames for spec compliance.","solutions":["Sort each '.'-separated component lexicographically before emitting the tag string.","If the order is intentional for your tool, omit validate_order (it defaults to False).","Use parse_tag without validate_order for tolerant parsing, and apply ordering only when emitting canonical tags.","Catch UnsortedTagsError and re-sort the component automatically, then re-parse."],"exampleFix":"# before\nparse_tag('py3.py2-none-any', validate_order=True)  # UnsortedTagsError\n\n# after - emit components in sorted order\ncomp = '.'.join(sorted(['py3', 'py2']))  # 'py2.py3'\nparse_tag(f'{comp}-none-any', validate_order=True)","handlingStrategy":"validation","validationCode":"def sorted_component(comp: str) -> str:\n    return '.'.join(sorted(comp.split('.')))\n\n# then build the tag with each component pre-sorted","typeGuard":"def components_sorted(tag: str) -> bool:\n    return all(p.split('.') == sorted(p.split('.')) for p in tag.split('-'))","tryCatchPattern":"from packaging.tags import UnsortedTagsError\ntry:\n    parse_tag(tag, validate_order=True)\nexcept UnsortedTagsError:\n    tag = '-'.join(sorted_component(c) for c in tag.split('-'))\n    parse_tag(tag, validate_order=True)","preventionTips":["Always emit compressed components in sorted order.","Only enable validate_order when spec compliance is required.","Auto-sort offending components and re-parse."],"tags":["packaging","tags","pep425","validation","ordering"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}