{"record":{"id":"577d57f8b3a44f25","repo":"pypa/pip","slug":"dist-info-directory-info-dir-r-does-not-start-w","errorCode":null,"errorMessage":".dist-info directory {info_dir!r} does not start with {canonical_name!r}","messagePattern":"\\.dist-info directory (.+?) does not start with (.+?)","errorType":"exception","errorClass":"UnsupportedWheel","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/utils/wheel.py","lineNumber":60,"sourceCode":"    # Zip file path separators must be /\n    subdirs = {p.split(\"/\", 1)[0] for p in source.namelist()}\n\n    info_dirs = [s for s in subdirs if s.endswith(\".dist-info\")]\n\n    if not info_dirs:\n        raise UnsupportedWheel(\".dist-info directory not found\")\n\n    if len(info_dirs) > 1:\n        raise UnsupportedWheel(\n            \"multiple .dist-info directories found: {}\".format(\", \".join(info_dirs))\n        )\n\n    info_dir = info_dirs[0]\n\n    info_dir_name = canonicalize_name(info_dir)\n    canonical_name = canonicalize_name(name)\n    if not info_dir_name.startswith(canonical_name):\n        raise UnsupportedWheel(\n            f\".dist-info directory {info_dir!r} does not start with {canonical_name!r}\"\n        )\n\n    return info_dir\n\n\ndef read_wheel_metadata_file(source: ZipFile, path: str) -> bytes:\n    try:\n        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.","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_internal/utils/wheel.py#L42-L78","documentation":"Raised by wheel_dist_info_dir() when the single .dist-info directory name, after canonicalization (PEP 503 normalization), does not start with the canonicalized wheel name. This check ensures the metadata directory corresponds to the distribution being installed, catching mismatches between the wheel filename and its internal metadata.","triggerScenarios":"Calling wheel_dist_info_dir(source, name) where canonicalize_name(info_dir) does not start with canonicalize_name(name). For example, a wheel named 'foo-bar-1.0.whl' whose .dist-info is 'baz-1.0.dist-info'.","commonSituations":"A wheel manually renamed to a different distribution name without rebuilding. A build backend configured with the wrong project name. A vendored or repackaged wheel whose filename was changed but contents were not. A typo-squatting package mimicking another name.","solutions":["Compare the wheel filename's project name with the .dist-info directory name inside it","Rebuild the wheel ensuring the project name in pyproject.toml/setup.py matches the filename","Re-download the wheel from the official index to ensure filename and contents are consistent","Do not manually rename wheel files; rebuild them with the correct name"],"exampleFix":"// before\n# wheel renamed from old-name-1.0.whl to new-name-1.0.whl\npip install ./new-name-1.0-py3-none-any.whl\n// after\n# set the correct project name in pyproject.toml then rebuild\n[project]\nname = \"new-name\"\npython -m build --wheel\npip install dist/new-name-1.0-py3-none-any.whl","handlingStrategy":"validation","validationCode":"import zipfile\nfrom pip._vendor.packaging.utils import canonicalize_name\nimport os\n\ndef wheel_name_matches_dist_info(path: str) -> bool:\n    # extract project name from filename: name-version-pythontag-abitag-plattag.whl\n    wheel_name = os.path.basename(path).split('-', 1)[0]\n    canonical_wheel = canonicalize_name(wheel_name)\n    with zipfile.ZipFile(path) as zf:\n        dirs = [n.split('/')[0] for n in zf.namelist() if '/' in n]\n        info_dirs = [d for d in set(dirs) if d.endswith('.dist-info')]\n        if len(info_dirs) != 1:\n            return False\n        return canonicalize_name(info_dirs[0]).startswith(canonical_wheel)","typeGuard":"from pip._vendor.packaging.utils import canonicalize_name\n\ndef dist_info_matches_name(info_dir: str, wheel_name: str) -> bool:\n    return canonicalize_name(info_dir).startswith(canonicalize_name(wheel_name))","tryCatchPattern":"from pip._internal.exceptions import UnsupportedWheel\n\ntry:\n    wheel_dist_info_dir(wheel_zip, name)\nexcept UnsupportedWheel as e:\n    if 'does not start with' in str(e):\n        raise PackagingError(f'wheel filename/metadata mismatch: {e}')\n    raise","preventionTips":["Never manually rename wheel files","Ensure pyproject.toml/setup.py project name matches the output wheel name","Rebuild wheels with a standard backend after changing the project name","Verify filename-to-metadata consistency before publishing"],"tags":["wheel","metadata","naming","packaging","installation"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}