{"id":"52afb5c0571325bf","repo":"pypa/pip","slug":"invalid-wheel-version-version-r","errorCode":null,"errorMessage":"invalid Wheel-Version: {version!r}","messagePattern":"invalid Wheel-Version: (.+?)","errorType":"exception","errorClass":"UnsupportedWheel","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/utils/wheel.py","lineNumber":108,"sourceCode":"    # message may have .defects populated, but for backwards-compatibility we\n    # currently ignore them.\n    return Parser().parsestr(wheel_text)\n\n\ndef wheel_version(wheel_data: Message) -> tuple[int, ...]:\n    \"\"\"Given WHEEL metadata, return the parsed Wheel-Version.\n    Otherwise, raise UnsupportedWheel.\n    \"\"\"\n    version_text = wheel_data[\"Wheel-Version\"]\n    if version_text is None:\n        raise UnsupportedWheel(\"WHEEL is missing Wheel-Version\")\n\n    version = version_text.strip()\n\n    try:\n        return tuple(map(int, version.split(\".\")))\n    except ValueError:\n        raise UnsupportedWheel(f\"invalid Wheel-Version: {version!r}\")\n\n\ndef check_compatibility(version: tuple[int, ...], name: str) -> None:\n    \"\"\"Raises errors or warns if called with an incompatible Wheel-Version.\n\n    pip should refuse to install a Wheel-Version that's a major series\n    ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when\n    installing a version only minor version ahead (e.g 1.2 > 1.1).\n\n    version: a 2-tuple representing a Wheel-Version (Major, Minor)\n    name: name of wheel or package to raise exception about\n\n    :raises UnsupportedWheel: when an incompatible Wheel-Version is given\n    \"\"\"\n    if version[0] > VERSION_COMPATIBLE[0]:\n        raise UnsupportedWheel(\n            \"{}'s Wheel-Version ({}) is not compatible with this version \"\n            \"of pip\".format(name, \".\".join(map(str, version)))","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_internal/utils/wheel.py#L90-L126","documentation":"UnsupportedWheel raised when the Wheel-Version header is present but cannot be parsed into a tuple of integers. After stripping, pip does tuple(map(int, version.split('.'))); a ValueError (non-numeric components) is wrapped with the offending version string repr. The wheel spec requires a numeric Major.Minor version.","triggerScenarios":"wheel_version sees a Wheel-Version like '1.0rc1', 'v1.0', '1.a', or '' and int() fails on a component.","commonSituations":"Hand-written WHEEL with a non-numeric version; tooling that appended a suffix; tampering; backend bug.","solutions":["Inspect: `unzip -p pkg.whl '*/WHEEL' | grep Wheel-Version`.","Set Wheel-Version to a plain numeric value like '1.0'.","Rebuild with a standard backend.","Run `twine check` before install."],"exampleFix":"// before\n# WHEEL: Wheel-Version: 1.0-beta\npip install pkg-1.0-py3-none-any.whl\n\n// after\n# WHEEL: Wheel-Version: 1.0\nrm -rf dist && python -m build --wheel","handlingStrategy":"validation","validationCode":"import zipfile, email.parser\ndef wheel_version_parses(path: str) -> bool:\n    with zipfile.ZipFile(path) as z:\n        entry = next((n for n in z.namelist() if n.endswith('/WHEEL')), None)\n        msg = email.parser.Parser().parsestr(z.read(entry).decode('utf-8'))\n    v = (msg['Wheel-Version'] or '').strip()\n    try:\n        tuple(map(int, v.split('.')))\n        return True\n    except ValueError:\n        return False","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Use Wheel-Version: 1.0 (numeric only).","Use a PEP 517 backend.","Run `twine check`.","Avoid suffixes/pre-release strings on Wheel-Version."],"tags":["pip","wheel","metadata","validation","version"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}