{"record":{"id":"cb8e16e4049defdc","repo":"pypa/pip","slug":"invalid-wheel-filename-invalid-tag-component-f","errorCode":null,"errorMessage":"Invalid wheel filename (invalid tag component): {filename!r}","messagePattern":"Invalid wheel filename \\(invalid tag component\\): (.+?)","errorType":"validation","errorClass":"InvalidWheelFilename","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/utils.py","lineNumber":300,"sourceCode":"        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.\n    :raises InvalidSdistFilename: If the filename does not end\n        with an sdist extension (``.zip`` or ``.tar.gz``), if it does not\n        contain a dash separating the name and the version of the distribution,\n        if the project name is empty, or if the version portion is not a valid\n        version.","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/utils.py#L282-L318","documentation":"Raised by parse_wheel_filename() when parse_tag() raises InvalidTag (utils.py:299-302). This covers three cases per tags.py:301-304: the tag string does not have exactly three dash-separated components, any component is empty (e.g. trailing dash or double dash), or an interpreter component is not a valid Python identifier.","triggerScenarios":"A wheel filename whose tag portion is malformed: 'foo-1.0-py3-none-any-extra.whl' (4 components), 'foo-1.0-py3--any.whl' (empty ABI), or 'foo-1.0-3py-none-any.whl' (interpreter '3py' is not a valid identifier because it starts with a digit).","commonSituations":"Hand-crafted wheel filenames with wrong tag structure, a platform tag containing a stray dash, or a build tool that omits or duplicates a tag component.","solutions":["Ensure the tag portion has exactly three components: {pythontag}-{abitag}-{platformtag}.","Remove any empty components (no double dashes or trailing dashes).","Verify the interpreter tag starts with a letter or underscore (must be a valid Python identifier)."],"exampleFix":"// before\nparse_wheel_filename('foo-1.0-py3-none-any-extra.whl')  // 4 tag components\n// after\nparse_wheel_filename('foo-1.0-py3-none-any.whl')","handlingStrategy":"validation","validationCode":"def safe_parse_wheel(filename: str):\n    stem = filename[:-4]\n    tag_str = stem.rsplit(\"-\", 3)[-1]\n    components = tag_str.split(\"-\")\n    if len(components) != 3:\n        raise ValueError(f\"Tag must have 3 components: {tag_str!r}\")\n    for comp in components:\n        if \"\" in comp.split(\".\"):\n            raise ValueError(f\"Empty tag component in: {tag_str!r}\")\n    for interp in components[0].split(\".\"):\n        if not interp.isidentifier():\n            raise ValueError(f\"Invalid interpreter tag: {interp!r}\")\n    return parse_wheel_filename(filename)","typeGuard":"def is_valid_tag_string(tag_str: str) -> bool:\n    parts = tag_str.split(\"-\")\n    if len(parts) != 3:\n        return False\n    for component in parts:\n        if \"\" in component.split(\".\"):\n            return False\n    return all(interp.isidentifier() for interp in parts[0].split(\".\"))","tryCatchPattern":"from packaging.utils import InvalidWheelFilename\n\ntry:\n    name, ver, build, tags = parse_wheel_filename(filename)\nexcept InvalidWheelFilename as e:\n    if \"invalid tag component\" in str(e):\n        logger.error(\"Malformed tag in wheel: %s\", filename)\n    raise","preventionTips":["Verify the tag portion has exactly three dash-separated components","Ensure no empty components (no double dashes)","Check that the interpreter tag starts with a letter or underscore (valid Python identifier)"],"tags":["wheel","filename","tags","packaging","pep425"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}