{"id":"430e370bb5ac46dd","repo":"pypa/pip","slug":"error-decoding-path-r-e-r","errorCode":null,"errorMessage":"error decoding {path!r}: {e!r}","messagePattern":"error decoding (.+?): (.+?)","errorType":"exception","errorClass":"UnsupportedWheel","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/utils/wheel.py","lineNumber":87,"sourceCode":"        return source.read(path)\n        # BadZipFile for general corruption, KeyError for missing entry,\n        # and RuntimeError for password-protected files\n    except (BadZipFile, KeyError, RuntimeError) as e:\n        raise UnsupportedWheel(f\"could not read {path!r} file: {e!r}\")\n\n\ndef wheel_metadata(source: ZipFile, dist_info_dir: str) -> Message:\n    \"\"\"Return the WHEEL metadata of an extracted wheel, if possible.\n    Otherwise, raise UnsupportedWheel.\n    \"\"\"\n    path = f\"{dist_info_dir}/WHEEL\"\n    # Zip file path separators must be /\n    wheel_contents = read_wheel_metadata_file(source, path)\n\n    try:\n        wheel_text = wheel_contents.decode()\n    except UnicodeDecodeError as e:\n        raise UnsupportedWheel(f\"error decoding {path!r}: {e!r}\")\n\n    # FeedParser (used by Parser) does not raise any exceptions. The returned\n    # 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:","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_internal/utils/wheel.py#L69-L105","documentation":"UnsupportedWheel raised when the WHEEL metadata file was read successfully (raw bytes) but cannot be decoded as text. pip calls wheel_contents.decode() expecting UTF-8; a UnicodeDecodeError is wrapped with the path and the error repr. It indicates the WHEEL file is binary garbage or in an unexpected encoding.","triggerScenarios":"wheel_metadata reads <dist-info>/WHEEL bytes, then .decode() raises UnicodeDecodeError because the bytes aren't valid UTF-8 (e.g. a binary placeholder, encryption artifact, or wrong file written into the WHEEL slot).","commonSituations":"Wheel build script that wrote binary data into the WHEEL path; corrupt download leaving non-UTF-8 bytes; a tool that accidentally included the wrong file as WHEEL.","solutions":["Open the WHEEL entry manually: `unzip -p pkg.whl '*/WHEEL' | head` and check it is readable text.","Rebuild the wheel ensuring WHEEL is plain UTF-8 text generated by the backend.","Re-download and verify the hash.","Inspect with `twine check` which validates wheel metadata before publishing."],"exampleFix":"// before\n# custom build wrote a binary blob as WHEEL\npip install pkg-1.0-py3-none-any.whl\n\n// after\n# let the backend write WHEEL correctly; rebuild:\nrm -rf dist && python -m build --wheel && twine check dist/*","handlingStrategy":"validation","validationCode":"import zipfile\ndef wheel_file_decodes(path: str) -> bool:\n    with zipfile.ZipFile(path) as z:\n        wheel_entry = next((n for n in z.namelist() if n.endswith('/WHEEL')), None)\n        if not wheel_entry:\n            return False\n        try:\n            z.read(wheel_entry).decode('utf-8')\n            return True\n        except UnicodeDecodeError:\n            return False","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Use a PEP 517 backend to generate WHEEL as UTF-8 text.","Run `twine check` to validate metadata encoding.","Never write binary into WHEEL during custom builds.","Verify download integrity."],"tags":["pip","wheel","encoding","metadata","validation"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}