{"id":"644c4d38196042d5","repo":"pypa/pip","slug":"invalid-wheel-filename-wrong-number-of-parts-f","errorCode":null,"errorMessage":"Invalid wheel filename (wrong number of parts): {filename!r}","messagePattern":"Invalid wheel filename \\(wrong number of parts\\): (.+?)","errorType":"validation","errorClass":"InvalidWheelFilename","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/utils.py","lineNumber":210,"sourceCode":"    >>> ver == Version('1.0')\n    True\n    >>> tags == {Tag(\"py3\", \"none\", \"any\")}\n    True\n    >>> not build\n    True\n\n    .. versionadded:: 26.1\n       The *validate_order* parameter.\n    \"\"\"\n    if not filename.endswith(\".whl\"):\n        raise InvalidWheelFilename(\n            f\"Invalid wheel filename (extension must be '.whl'): {filename!r}\"\n        )\n\n    filename = filename[:-4]\n    dashes = filename.count(\"-\")\n    if dashes not in (4, 5):\n        raise InvalidWheelFilename(\n            f\"Invalid wheel filename (wrong number of parts): {filename!r}\"\n        )\n\n    parts = filename.split(\"-\", dashes - 2)\n    name_part = parts[0]\n    # See PEP 427 for the rules on escaping the project name.\n    if \"__\" in name_part or re.match(r\"^[\\w\\d._]*$\", name_part, re.UNICODE) is None:\n        raise InvalidWheelFilename(f\"Invalid project name: {filename!r}\")\n    name = canonicalize_name(name_part)\n\n    try:\n        version = Version(parts[1])\n    except InvalidVersion as e:\n        raise InvalidWheelFilename(\n            f\"Invalid wheel filename (invalid version): {filename!r}\"\n        ) from e\n\n    if dashes == 5:","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/packaging/utils.py#L192-L228","documentation":"Raised by `parse_wheel_filename` when the stem (filename minus `.whl`) does not contain exactly 4 or 5 dashes. A well-formed wheel filename has 5 dash-separated parts (name, version, pytag, abitag, platformtag) or 6 when a build tag is present, so after stripping `.whl` the dash count must be 4 (no build) or 5 (with build). Any other count means the filename is structurally broken.","triggerScenarios":"A wheel filename with a project name or version containing literal dashes that were not escaped to underscores during build, or a missing/extra tag component. Example: `my-pkg-1.0-py3-none-any.whl` (the unescaped dash in `my-pkg` inflates the dash count).","commonSituations":"Hand-renamed wheel files; project names built with hyphens that the build backend failed to normalize; truncation/corruption of the filename; mixing in non-wheel files that happen to end in `.whl`.","solutions":["Rebuild the wheel with a compliant backend (setuptools/hatchling/flit) which escapes `-` in the name to `_`.","Rename so name/version contain no unescaped dashes: replace `-` with `_` in the name and version segments, e.g. `my_pkg-1.0-py3-none-any.whl`.","Confirm the expected 5-part (or 6-part with build) structure before parsing.","Discard/re-download the artifact if externally produced and malformed."],"exampleFix":"// before\nparse_wheel_filename('my-pkg-1.0-py3-none-any.whl')  # 5 dashes -> raises\n\n# after\nparse_wheel_filename('my_pkg-1.0-py3-none-any.whl')  # 4 dashes -> ok","handlingStrategy":"try-catch","validationCode":"def has_wheel_dash_count(filename: str) -> bool:\n    if not filename.endswith('.whl'):\n        return False\n    return filename[:-4].count('-') in (4, 5)","typeGuard":"def is_well_formed_wheel(filename: object) -> bool:\n    if not isinstance(filename, str) or not filename.endswith('.whl'):\n        return False\n    return filename[:-4].count('-') in (4, 5)","tryCatchPattern":"from pip._vendor.packaging.utils import parse_wheel_filename, InvalidWheelFilename\n\ntry:\n    name, ver, build, tags = parse_wheel_filename(filename)\nexcept InvalidWheelFilename as e:\n    if 'wrong number of parts' in str(e):\n        # rebuild or rename so the name has no unescaped dashes\n        ...\n    raise","preventionTips":["Use a compliant build backend so project-name dashes are escaped to underscores in wheel filenames.","Never hand-edit wheel filenames.","Pre-validate the dash count is 4 or 5 after stripping `.whl`."],"tags":["python","packaging","utils","wheel","pep427","filename"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}