{"id":"1a0adb70872ecf69","repo":"pypa/pip","slug":"uninstall-distutils-installed-package","errorCode":"uninstall-distutils-installed-package","errorMessage":"Cannot uninstall {distribution}","messagePattern":"Cannot uninstall (.+?)","errorType":"exception","errorClass":"LegacyDistutilsInstall","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/req/req_uninstall.py","lineNumber":510,"sourceCode":"                try:\n                    namespace_packages = dist.read_text(\"namespace_packages.txt\")\n                except FileNotFoundError:\n                    namespaces = []\n                else:\n                    namespaces = namespace_packages.splitlines(keepends=False)\n                for top_level_pkg in [\n                    p\n                    for p in dist.read_text(\"top_level.txt\").splitlines()\n                    if p and p not in namespaces\n                ]:\n                    path = os.path.join(dist_location, top_level_pkg)\n                    paths_to_remove.add(path)\n                    paths_to_remove.add(f\"{path}.py\")\n                    paths_to_remove.add(f\"{path}.pyc\")\n                    paths_to_remove.add(f\"{path}.pyo\")\n\n        elif dist.installed_by_distutils:\n            raise LegacyDistutilsInstall(distribution=dist)\n\n        elif dist.installed_as_egg:\n            # package installed by easy_install\n            # We cannot match on dist.egg_name because it can slightly vary\n            # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg\n            # XXX We use normalized_dist_location because dist_location my contain\n            # a trailing / if the distribution is a zipped egg\n            # (which is not a directory).\n            paths_to_remove.add(normalized_dist_location)\n            easy_install_egg = os.path.split(normalized_dist_location)[1]\n            easy_install_pth = os.path.join(\n                os.path.dirname(normalized_dist_location),\n                \"easy-install.pth\",\n            )\n            paths_to_remove.add_pth(easy_install_pth, \"./\" + easy_install_egg)\n\n        elif dist.installed_with_dist_info:\n            for path in uninstallation_paths(dist):","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_internal/req/req_uninstall.py#L492-L528","documentation":"LegacyDistutilsInstall (InstallationError subclass with code 'uninstall-distutils-installed-package') raised during uninstall when the distribution was installed by plain distutils ('python setup.py install') and pip cannot safely enumerate its files. pip detects this via dist.installed_by_distutils and refuses, pointing the user at manual removal.","triggerScenarios":"'pip uninstall <pkg>' where <pkg> was installed by distutils (no .egg-info/RECORD usable by pip). The code path lands in the 'elif dist.installed_by_distutils' branch and raises.","commonSituations":"Legacy packages installed before pip was the norm; system Python with packages installed via 'python setup.py install'; tutorials that still recommend setup.py install.","solutions":["Manually remove the package files and its .egg-info directory (find them with 'python -c \"import <pkg>; print(<pkg>.__path__)\"').","Reinstall with pip ('pip install --force-reinstall --no-deps <pkg>') to get a RECORD, then 'pip uninstall <pkg>'.","If installed system-wide via OS Python, prefer the OS package manager to remove it.","Stop using 'python setup.py install'; use 'pip install .' for future installs."],"exampleFix":"# before\npip uninstall legacy-pkg   # LegacyDistutilsInstall\n\n# after\npip install --force-reinstall --no-deps legacy-pkg\npip uninstall legacy-pkg","handlingStrategy":"try-catch","validationCode":"def installed_by_distutils(dist) -> bool:\n    return getattr(dist, 'installed_by_distutils', False)","typeGuard":"def is_pip_managed(dist) -> bool:\n    return not getattr(dist, 'installed_by_distutils', False) and not getattr(dist, 'installed_as_egg', False)","tryCatchPattern":"from pip._internal.exceptions import LegacyDistutilsInstall\ntry:\n    run_pip(['uninstall', '-y', 'pkg'])\nexcept LegacyDistutilsInstall:\n    print('distutils install; reinstall via pip then uninstall')\n    run_pip(['install', '--force-reinstall', '--no-deps', 'pkg'])\n    run_pip(['uninstall', '-y', 'pkg'])","preventionTips":["Stop using 'python setup.py install'; always 'pip install'.","Audit 'pip list' for distutils-installed packages before uninstall campaigns.","Prefer OS package manager for OS-shipped packages."],"tags":["uninstall","distutils","legacy","metadata"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}