ModuleNotFoundError: No module named 'distutils' (tested fix)

ModuleNotFoundError: No module named 'distutils' means the code (or a package pip is building) runs on Python 3.12 or newer, where distutils no longer exists. We ran the import and both fixes on 3.11, 3.13 and 3.14.

1. Cause: distutils was removed in Python 3.12

import sys
print(sys.version.split()[0])
try:
    from distutils.version import LooseVersion
    print("distutils OK:", LooseVersion("1.10") > LooseVersion("1.9"))
except ModuleNotFoundError as e:
    print(f"ModuleNotFoundError: {e}")

Output on Python 3.13.5 (python:3-alpine with 3.14.7 says the same):

3.13.5
ModuleNotFoundError: No module named 'distutils'

Output on the official python:3.11-alpine image:

/w/b1.py:5: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
  print("distutils OK:", LooseVersion("1.10") > LooseVersion("1.9"))
3.11.17
distutils OK: True

distutils was deprecated by PEP 632 and deleted from the standard library in Python 3.12. Code that imports it, or an old package whose setup.py imports it while pip builds it from source, works on 3.11 and fails on 3.12+. Check python3 --version where it fails: a new Docker base image or CI runner is the usual trigger.

2. Quick fix: install setuptools (it ships a distutils shim)

We ran this script in a fresh python:3-alpine container (Python 3.14.7, which has no setuptools preinstalled):

python -c "import setuptools" 2>&1 | tail -1
pip install -q --disable-pip-version-check --root-user-action=ignore setuptools 2>&1 | tail -1
pip show setuptools 2>/dev/null | grep -E '^(Name|Version)'
python -c "import distutils, sys; print(sys.version.split()[0], distutils.__file__)"
python -c "from distutils.version import LooseVersion; print(LooseVersion('1.10') > LooseVersion('1.9'))" 2>&1

Output:

ModuleNotFoundError: No module named 'setuptools'
Name: setuptools
Version: 84.2.1
3.14.7 /usr/local/lib/python3.14/site-packages/setuptools/_distutils/__init__.py
<string>:1: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
True

After pip install setuptools, import distutils resolves to setuptools/_distutils, so legacy imports work again. Note that Python 3.12+ images and venvs no longer include setuptools by default, which is why the error appears on a clean install. If the error comes from pip building an old package, upgrading that package to a release with wheels for your Python is often the better fix.

3. Proper fix: replace the distutils calls

from packaging.version import Version
print(Version("1.10") > Version("1.9"))
print(Version("2.0.0rc1") < Version("2.0.0"))
import shutil, sysconfig
print(shutil.which("sh"))                        # was distutils.spawn.find_executable
print(sysconfig.get_paths()["purelib"])         # was distutils.sysconfig.get_python_lib()

Output (Python 3.14.7, after pip install packaging):

True
True
/bin/sh
/usr/local/lib/python3.14/site-packages

The common replacements: distutils.version.LooseVersion/StrictVersion → packaging.version.Version; distutils.spawn.find_executable → shutil.which; distutils.sysconfig.get_python_lib() → sysconfig.get_paths()["purelib"]; distutils.util.strtobool has no direct replacement, so write a small function.

Related: ModuleNotFoundError vs ImportError · name 'np' is not defined.

Frequently asked questions

Why is there no module named distutils?

distutils was removed from the standard library in Python 3.12 (PEP 632). It still exists on 3.11 and older.

How do I fix No module named distutils?

pip install setuptools: it provides a distutils shim. Long term, replace LooseVersion with packaging.version.Version and find_executable with shutil.which.

Can I pip install distutils?

Do not look for a distutils package on PyPI. On 3.12+, installing setuptools is the supported way to get the distutils module back.

Written with AI assistance. Measured on 2026-10-22 with Python 3.13.5 on Debian and the official python:3.11-alpine (3.11.17) and python:3-alpine (3.14.7) images: importing distutils, pip install setuptools 84.2.1 as a shim, and packaging/shutil/sysconfig replacements. Every block was run verbatim and its output pasted unedited. PlainHeaders is our extension; Python, setuptools and packaging are free.