{"record":{"id":"46546ce6e755dffe","repo":"pypa/pip","slug":"invalid-wheel-filename-compressed-tag-set-compone","errorCode":null,"errorMessage":"Invalid wheel filename (compressed tag set components must be in sorted order per PEP 425): {filename!r}","messagePattern":"Invalid wheel filename \\(compressed tag set components must be in sorted order per PEP 425\\): (.+?)","errorType":"validation","errorClass":"InvalidWheelFilename","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/utils.py","lineNumber":295,"sourceCode":"        raise InvalidWheelFilename(\n            f\"Invalid wheel filename (invalid version): {filename!r}\"\n        ) from e\n\n    if dashes == 5:\n        build_part = parts[2]\n        build_match = _build_tag_regex.match(build_part)\n        if build_match is None:\n            raise InvalidWheelFilename(\n                f\"Invalid build number: {build_part} in {filename!r}\"\n            )\n        build = cast(\"BuildTag\", (int(build_match.group(1)), build_match.group(2)))\n    else:\n        build = ()\n    tag_str = parts[-1]\n    try:\n        tags = parse_tag(tag_str, validate_order=validate_order)\n    except UnsortedTagsError:\n        raise InvalidWheelFilename(\n            f\"Invalid wheel filename (compressed tag set components must be in \"\n            f\"sorted order per PEP 425): {filename!r}\"\n        ) from None\n    except InvalidTag:\n        raise InvalidWheelFilename(\n            f\"Invalid wheel filename (invalid tag component): {filename!r}\"\n        ) from None\n    return (name, version, build, tags)\n\n\ndef parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]:\n    \"\"\"\n    This function takes the filename of a sdist file (as specified\n    in the `Source distribution format`_ documentation), and parses\n    it, returning a tuple of the normalized name and version as\n    represented by an instance of :class:`~packaging.version.Version`.\n\n    :param str filename: The name of the sdist file.","sourceCodeStart":277,"sourceCodeEnd":313,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/utils.py#L277-L313","documentation":"Raised by parse_wheel_filename() only when validate_order=True and the underlying parse_tag() detects that a compressed tag-set component is not in ascending sorted order (utils.py:294-298). Per PEP 425, members of a compressed tag set (e.g. the '.py2.py3' in 'py2.py3-none-any') must appear in sorted order. parse_tag raises UnsortedTagsError, which is re-raised as InvalidWheelFilename.","triggerScenarios":"Calling parse_wheel_filename('foo-1.0-py3.py2-none-any.whl', validate_order=True) where 'py3.py2' is not sorted (should be 'py2.py3').","commonSituations":"A wheel built by a tool that does not sort compressed tag-set members, or a manually constructed tag string where components were added out of order. Only triggered when the caller explicitly opts into order validation.","solutions":["Sort each compressed tag-set component alphabetically (e.g. 'py3.py2' -> 'py2.py3').","If order validation is not required for your use case, omit validate_order=True (defaults to False).","Regenerate the wheel with a conforming build backend."],"exampleFix":"// before\nparse_wheel_filename('foo-1.0-py3.py2-none-any.whl', validate_order=True)\n// after\nparse_wheel_filename('foo-1.0-py2.py3-none-any.whl', validate_order=True)","handlingStrategy":"validation","validationCode":"def safe_parse_wheel(filename: str, validate_order: bool = False):\n    if validate_order:\n        stem = filename[:-4]\n        tag_str = stem.rsplit(\"-\", 3)[-1]\n        for component in tag_str.split(\"-\"):\n            members = component.split(\".\")\n            if members != sorted(members):\n                raise ValueError(f\"Tag component not sorted: {component!r}\")\n    return parse_wheel_filename(filename, validate_order=validate_order)","typeGuard":"def tags_are_sorted(tag_str: str) -> bool:\n    return all(\n        parts == sorted(parts)\n        for parts in (c.split(\".\") for c in tag_str.split(\"-\"))\n    )","tryCatchPattern":"from packaging.utils import InvalidWheelFilename\n\ntry:\n    parse_wheel_filename(filename, validate_order=True)\nexcept InvalidWheelFilename as e:\n    if \"sorted order\" in str(e):\n        logger.warning(\"Tag set not sorted in %s, retrying without validation\", filename)\n        parse_wheel_filename(filename, validate_order=False)","preventionTips":["Sort each compressed tag-set component alphabetically before embedding in a filename","Only pass validate_order=True when strict PEP 425 compliance is required","Use a conforming build backend that emits sorted tag sets"],"tags":["wheel","filename","tags","pep425","packaging"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}