pypa/pip · error · LegacyDistutilsInstall
uninstall-distutils-installed-package
uninstall-distutils-installed-package
Error message
Cannot uninstall {distribution} What it means
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.
Source
Thrown at src/pip/_internal/req/req_uninstall.py:510
try:
namespace_packages = dist.read_text("namespace_packages.txt")
except FileNotFoundError:
namespaces = []
else:
namespaces = namespace_packages.splitlines(keepends=False)
for top_level_pkg in [
p
for p in dist.read_text("top_level.txt").splitlines()
if p and p not in namespaces
]:
path = os.path.join(dist_location, top_level_pkg)
paths_to_remove.add(path)
paths_to_remove.add(f"{path}.py")
paths_to_remove.add(f"{path}.pyc")
paths_to_remove.add(f"{path}.pyo")
elif dist.installed_by_distutils:
raise LegacyDistutilsInstall(distribution=dist)
elif dist.installed_as_egg:
# package installed by easy_install
# We cannot match on dist.egg_name because it can slightly vary
# i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg
# XXX We use normalized_dist_location because dist_location my contain
# a trailing / if the distribution is a zipped egg
# (which is not a directory).
paths_to_remove.add(normalized_dist_location)
easy_install_egg = os.path.split(normalized_dist_location)[1]
easy_install_pth = os.path.join(
os.path.dirname(normalized_dist_location),
"easy-install.pth",
)
paths_to_remove.add_pth(easy_install_pth, "./" + easy_install_egg)
elif dist.installed_with_dist_info:
for path in uninstallation_paths(dist):View on GitHub (pinned to d7d0d0a394)
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.
Example fix
# before pip uninstall legacy-pkg # LegacyDistutilsInstall # after pip install --force-reinstall --no-deps legacy-pkg pip uninstall legacy-pkg
Defensive patterns
Strategy: try-catch
Validate before calling
def installed_by_distutils(dist) -> bool:
return getattr(dist, 'installed_by_distutils', False) Type guard
def is_pip_managed(dist) -> bool:
return not getattr(dist, 'installed_by_distutils', False) and not getattr(dist, 'installed_as_egg', False) Try / catch
from pip._internal.exceptions import LegacyDistutilsInstall
try:
run_pip(['uninstall', '-y', 'pkg'])
except LegacyDistutilsInstall:
print('distutils install; reinstall via pip then uninstall')
run_pip(['install', '--force-reinstall', '--no-deps', 'pkg'])
run_pip(['uninstall', '-y', 'pkg']) Prevention
- 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.
When it happens
Trigger: '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.
Common situations: 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.
Related errors
- uninstall-no-record-file
- You must give at least one requirement to {self.name} (see "
- <dynamic: upload-time missing detail>
- Bad metadata in {dist} (invalid metadata entry 'name')
- Error decoding metadata for {wheel}: {e} in {filename} file
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/1a0adb70872ecf69.json.
Report an issue: GitHub.