Skip to content

Fix: ERROR: Could not build wheels / Failed building wheel (pip)

FixDevs · (Updated: )

Part of:  Python Errors

Quick Answer

How to fix pip 'ERROR: Could not build wheels', 'Failed building wheel', 'No matching distribution found', and 'error: subprocess-exited-with-error'. Covers missing C compilers, build tools, system libraries, Python version issues, pre-built wheels, and platform-specific fixes for Linux, macOS, and Windows.

The Wheel That Never Got Built

Personally, I rate this as one of the most cryptic pip errors because the verbose output buries the actual cause. “Could not build wheels” almost always means “no pre-built binary for your platform, AND I cannot compile from source.” The fix depends on which half of that statement is true. I have learned to read the compiler output above the pip error, not the pip error itself. You run pip install and get one of these:

ERROR: Could not build wheels for numpy, which is required to install pyproject.toml-based projects
Failed building wheel for cryptography
error: subprocess-exited-with-error

× Building wheel for PyYAML (pyproject.toml) did not run successfully.
│ exit code: 1
╰─> [25 lines of output]
    ...
    error: command 'gcc' failed: No such file or directory
ERROR: No matching distribution found for tensorflow==2.15.0

These errors all point to the same core problem: pip can’t install a package because it can’t build it from source, can’t find a pre-built binary (wheel) for your platform, or the package doesn’t support your Python version.

Why pip Falls Back to Building from Source

Most Python packages on PyPI are distributed as wheels—pre-built binary packages that install instantly without compilation. But wheels are platform-specific. A wheel built for Linux on x86_64 won’t work on macOS on Apple Silicon.

When pip can’t find a compatible wheel, it falls back to building the package from source. This requires:

  • A C/C++ compiler (gcc, clang, or MSVC)
  • Python development headers (python3-dev / python3-devel)
  • System libraries the package depends on (libffi, libssl, libxml2, etc.)
  • Build tools (make, cmake, pkg-config)

If any of these are missing, the build fails. The error messages are often long and buried in compiler output, but the root cause is almost always one of the above.

“No matching distribution found” is different. It means pip can’t find any version of the package (source or wheel) that matches your Python version, OS, or architecture. This typically happens when:

  • The package doesn’t support your Python version (e.g., Python 3.13 when the package only supports up to 3.12)
  • The package doesn’t have builds for your platform (e.g., ARM64 / Apple Silicon)
  • You misspelled the package name
  • The package has been removed from PyPI

Quick Reference Before You Dive In

If you arrived here from Google with a fresh wheel build failure, the five facts that resolve roughly 90 percent of cases:

  1. READ ABOVE the Could not build wheels line. The actual compiler error (missing header, wrong toolchain, no Rust) is buried 20 to 50 lines above. The pip user guide and the PEP 517 build system spec are the canonical references.
  2. Update pip FIRST. python -m pip install --upgrade pip setuptools wheel. Old pip versions cannot see newer manylinux_2_28 or manylinux_2_34 wheel tags and silently fall back to building from source.
  3. Install build tools matching your OS. Linux: build-essential + python3-dev. macOS: xcode-select --install. Windows: Visual Studio Build Tools with “Desktop development with C++.”
  4. --prefer-binary or --only-binary :all: forces wheel-only install. Useful when you know wheels exist for your platform and want to skip the build path entirely.
  5. cryptography, tokenizers, polars, ruff need a RUST compiler. Install rustup if you see “can’t find Rust compiler” or “Cargo, the Rust package manager, is not installed.”

The rest of this article walks through each cause in detail, plus the failure modes most other guides skip.

How Python’s Packaging Stack Changed: PEP 517/518, manylinux, and distutils Removal

The reason this error often spikes after a Python upgrade (even when nothing else on the system changed) is that Python’s packaging stack has moved through several major shifts in the last few years. Knowing which shift you crossed makes the failure obvious.

  • PEP 517 / PEP 518 (pyproject.toml-based builds). Before 2017, python setup.py install was the canonical build path. PEP 517/518 introduced pyproject.toml-driven builds with isolated build environments. pip 21.3 (October 2021) made PEP 517 the default. The benefit: reproducible builds. The cost: when a build tool (setuptools, hatchling, poetry-core) is missing or pinned wrong, the error message ends up nested two levels deep and looks like a compiler failure when it is actually a build-backend failure. Always read above the “Could not build wheels” line for the real cause.
  • manylinux tags evolution. Linux wheels carry an ABI tag that says which glibc and which set of system libraries they target.
    • manylinux1 (PEP 513, 2016): targeted CentOS 5, glibc 2.5. Effectively retired.
    • manylinux2010 (PEP 571, 2018): CentOS 6, glibc 2.12.
    • manylinux2014 (PEP 599, 2019): CentOS 7, glibc 2.17. Still the de facto baseline for older infrastructure.
    • manylinux_2_28 (PEP 600, 2019): based on AlmaLinux 8 / glibc 2.28. Most major projects (NumPy, SciPy, cryptography, lxml) moved here in 2023–2024.
    • manylinux_2_34 started appearing in late 2024 for projects that want modern glibc features. If your pip is older than 22.x, it does not understand _2_28 or _2_34 wheel tags and silently falls back to building from source. Always pip install --upgrade pip first.
  • Python 3.12 removed distutils from the standard library (PEP 632). Setup scripts that still do from distutils.core import setup raise ModuleNotFoundError: No module named 'distutils'. The fix is to migrate the package to setuptools, or install the setuptools compatibility shim before running the build. Packages last released before 2023 are the usual offenders.
  • Python 3.13 disabled the GIL as an opt-in build (PEP 703, “free-threaded Python”) in October 2024. Wheels built for the standard 3.13 do not necessarily run on the free-threaded 3.13t. If you installed python3.13t and a package fails to build, that is the reason; wait for the project to publish cp313t wheels.
  • Rust as a build dependency became mainstream. cryptography >= 3.4 (March 2021), pydantic v2 core, tokenizers, polars, and ruff all need a Rust toolchain to build from source. On Python 3.13 release day in October 2024, none of them had matching wheels for hours — installing on day one required rustc.
  • build-system.requires is honored from pip 19.0+ only. Very old pip versions ignore the section and try to import setuptools that is not present.

If the error appeared after a Python minor-version upgrade, check whether the wheel tag for your version exists on the PyPI release page before doing anything else.

When to Use Which Fix

The next eight sections cover the fixes in detail. The table below maps your situation to the recommended fix.

Your situationRecommended fixWhy
No compiler installedFix 1: install build tools per OSCannot build C extensions
Missing system library (libffi, libssl, etc.)Fix 2: install -dev / -devel packageHeaders required
Old pip cannot see new wheelsFix 3: pip install --upgrade pip setuptools wheelNew manylinux tags
Want to skip source build entirelyFix 4: --prefer-binary or --only-binary :all:Wheel-only mode
No matching distribution after Python upgradeFix 5: check Python version against package metadataVersion compatibility
Apple Silicon / ARM64 with no wheelFix 6: Conda / Miniforge, or Rosetta x86_64 PythonArchitecture-specific
Cython or setuptools mismatchFix 7: pin or upgrade explicitlyBuild tool version drift
System Python permission issuesFix 8: virtual environmentClean state, no PEP 668

If multiple rows apply, pick the topmost one.

Fix 1: Install Build Tools and a C Compiler

The most common cause. You’re missing the compiler and development tools needed to build C extensions.

Linux (Debian/Ubuntu)

sudo apt update
sudo apt install build-essential python3-dev

build-essential installs gcc, g++, make, and other essential build tools. python3-dev installs the Python header files needed to compile C extensions.

For a specific Python version:

sudo apt install python3.12-dev

Linux (Fedora/RHEL/CentOS)

sudo dnf groupinstall "Development Tools"
sudo dnf install python3-devel

Linux (Arch)

sudo pacman -S base-devel python

Arch ships Python headers with the main python package.

macOS

Install Xcode Command Line Tools:

xcode-select --install

This gives you clang, make, and the standard C headers. You don’t need the full Xcode IDE—the command line tools are sufficient.

If you already have them installed but still get errors after a macOS upgrade, reinstall:

sudo rm -rf /Library/Developer/CommandLineTools
xcode-select --install

Windows

Install Microsoft Visual Studio Build Tools:

  1. Download from https://visualstudio.microsoft.com/visual-cpp-build-tools/
  2. Run the installer
  3. Select “Desktop development with C++”
  4. Install and restart your terminal

Without these build tools, any package with C extensions will fail on Windows. The error usually looks like:

error: Microsoft Visual C++ 14.0 or greater is required.

A small observation: installing build-essential and python3-dev is the single most effective preventive measure on any Linux dev machine. It costs almost nothing in disk space, fixes “Could not build wheels” pre-emptively for the entire ecosystem, and lets you skip half the fixes below. I add it to every Dockerfile and every Vagrant bootstrap script as a matter of course.

Fix 2: Install Missing System Libraries

Some packages need specific system libraries. The error output usually tells you which one is missing, but it’s buried in the compiler output. Look for lines like fatal error: ffi.h: No such file or directory or Package libssl was not found.

Common packages and their system dependencies

cryptography:

# Debian/Ubuntu
sudo apt install build-essential libssl-dev libffi-dev python3-dev

# Fedora
sudo dnf install openssl-devel libffi-devel python3-devel

# macOS
brew install openssl libffi
export LDFLAGS="-L$(brew --prefix openssl)/lib"
export CPPFLAGS="-I$(brew --prefix openssl)/include"

lxml:

# Debian/Ubuntu
sudo apt install libxml2-dev libxslt1-dev python3-dev

# Fedora
sudo dnf install libxml2-devel libxslt-devel python3-devel

# macOS
brew install libxml2 libxslt

Pillow:

# Debian/Ubuntu
sudo apt install libjpeg-dev zlib1g-dev libfreetype-dev liblcms2-dev libwebp-dev

# Fedora
sudo dnf install libjpeg-devel zlib-devel freetype-devel lcms2-devel libwebp-devel

# macOS
brew install libjpeg zlib freetype

psycopg2:

# Debian/Ubuntu
sudo apt install libpq-dev python3-dev

# Fedora
sudo dnf install postgresql-devel python3-devel

# macOS
brew install postgresql

Tip: if a package has a pure-Python or pre-built alternative, use that instead. For example, psycopg2-binary ships pre-built wheels and doesn’t require libpq-dev:

pip install psycopg2-binary

mysqlclient:

# Debian/Ubuntu
sudo apt install default-libmysqlclient-dev build-essential python3-dev pkg-config

# Fedora
sudo dnf install mysql-devel python3-devel

# macOS
brew install mysql pkg-config

Fix 3: Upgrade pip, setuptools, and wheel

An outdated pip may not find wheels that exist on PyPI. Newer pip versions support more wheel formats (including newer manylinux tags) and handle build dependencies better.

python -m pip install --upgrade pip setuptools wheel

Then retry your install:

pip install <package>

This is especially important on older systems where the default pip might be years old. A pip from 2021 will not know about manylinux_2_28 wheels published in 2024.

Fix 4: Use Pre-built Wheels (—prefer-binary)

Force pip to prefer pre-built wheels over source distributions:

pip install --prefer-binary <package>

This tells pip to choose a wheel even if a newer source distribution is available. Useful when a package has wheels for most versions but the very latest release only has a source dist.

You can also force pip to only install from wheels and never attempt a source build:

pip install --only-binary :all: <package>

If no compatible wheel exists, this will fail with an error instead of attempting a doomed build.

Fix 5: Check Python Version Compatibility

The package may not support your Python version. This is the most common cause of “No matching distribution found.”

Check your Python version:

python --version

Then check what Python versions the package supports. Go to the package’s PyPI page (e.g., https://pypi.org/project/<package>/) and look at the “Requires Python” field and the available download files.

You can also check from the command line:

pip install <package>==

This intentionally errors but shows all available versions. Pick a version that supports your Python.

If you’re on Python 3.13 and the package only supports up to 3.12, you have two options:

  1. Wait for the package to release a new version with 3.13 support
  2. Use an older Python version that the package supports

For managing multiple Python versions, use pyenv (Linux/macOS) or download specific versions from python.org (Windows).

Fix 6: Platform-Specific Wheels (Apple Silicon / ARM64)

If you’re on Apple Silicon (M1/M2/M3/M4) or ARM64 Linux, some packages don’t have pre-built wheels for your architecture.

Apple Silicon (macOS ARM64)

Older packages may only ship x86_64 wheels. Options:

Install Rosetta 2 and use an x86_64 Python:

softwareupdate --install-rosetta

Then install the x86_64 version of Python from python.org (the “macOS 64-bit universal2 installer” supports both architectures).

Use Conda/Miniforge: Miniforge provides ARM64-native builds for most scientific Python packages:

# Install Miniforge (ARM64-native)
brew install miniforge
conda create -n myenv python=3.12
conda activate myenv
conda install numpy pandas scipy

ARM64 Linux (Raspberry Pi, AWS Graviton)

Many packages now have aarch64 wheels, but some still don’t. Install build dependencies and build from source:

sudo apt install build-essential python3-dev gfortran libopenblas-dev
pip install numpy

For scientific packages, Conda (via Miniforge) often has ARM64 builds before PyPI wheels are available.

A pattern I lean on whenever the toolchain looks daunting: pre-built alternative packages. psycopg2-binary replaces psycopg2, pymysql replaces mysqlclient, pillow ships wheels for almost every platform. The trade-off is usually a slightly larger install size and slightly less control over linking, which I happily pay to skip the C-toolchain debugging spiral.

Fix 7: Fix Cython / setuptools Version Issues

Some packages require specific versions of Cython or setuptools to build. If you see errors like Cython.Compiler.Errors.CompileError or setuptools.extern.packaging.version.InvalidVersion, try:

pip install --upgrade Cython setuptools

Some older packages break with newer setuptools. If upgrading doesn’t help, try pinning an older version:

pip install "setuptools<71"
pip install <package>

This is common with packages that haven’t updated their build configuration for newer setuptools versions, particularly after setuptools 71.0 removed deprecated features.

Fix 8: Use a Virtual Environment

If you’re installing into the system Python, you might have permission issues or conflicting versions that interfere with the build. A virtual environment gives you a clean slate.

python -m venv .venv
source .venv/bin/activate    # Linux/macOS
# .venv\Scripts\activate     # Windows

pip install --upgrade pip setuptools wheel
pip install <package>

This also avoids the externally-managed-environment error on modern Linux distributions.

Stranger Causes I Have Tracked Down

Read the actual error

The pip output is verbose, but the real error is usually near the end. Scroll up past the “ERROR: Could not build wheels” line and look for:

  • fatal error: *.h: No such file or directory: missing header file. Install the corresponding -dev / -devel package.
  • command 'gcc' failed: no C compiler installed.
  • pkg-config errors: install pkg-config and the library it’s looking for.
  • LINK : fatal error LNK*: Windows linker error. Reinstall Visual Studio Build Tools.
  • RuntimeError: CMake must be installed: install cmake (pip install cmake or use your system package manager).

Install cmake or meson

Some packages (like dlib, llvmlite) need cmake or meson to build:

pip install cmake meson-python meson
# Then retry
pip install <package>

Use Conda instead of pip

For scientific and data packages (NumPy, SciPy, TensorFlow, PyTorch, OpenCV), Conda often works when pip doesn’t. Conda ships its own pre-compiled binaries with all dependencies bundled, so you don’t need system libraries.

conda install numpy scipy pandas scikit-learn

Check for a binary variant of the package

Some hard-to-build packages offer a pre-built variant:

Hard to buildUse this instead
psycopg2psycopg2-binary
mysqlclientpymysql (pure Python)
lxmlpre-built wheels usually available; update pip first
opencv-pythonpre-built on most platforms; check opencv-python-headless
pillowpre-built wheels usually available; update pip first

The package is not on PyPI

“No matching distribution found” can also mean the package name is wrong or it’s hosted elsewhere. Check:

# Verify the package name
pip search <package>  # deprecated, use the PyPI website instead
pip install <package> --dry-run

Some packages are hosted on custom indexes. Check the project’s documentation for install instructions:

# Example: installing PyTorch from its own index
pip install torch --index-url https://download.pytorch.org/whl/cpu

Build isolation is interfering

pip builds packages in an isolated environment by default (PEP 517). Sometimes this causes issues if the package’s build dependencies have conflicts. Try disabling build isolation:

pip install --no-build-isolation <package>

You’ll need to manually install the package’s build dependencies first (check pyproject.toml for [build-system] requires).

You’re behind a proxy or firewall

If pip can’t download build dependencies, the build will fail. Configure pip to use your proxy:

pip install --proxy http://user:password@proxy.example.com:8080 <package>

Or set the environment variable:

export HTTPS_PROXY=http://proxy.example.com:8080
pip install <package>

Rust compiler required

Some packages (like cryptography >= 3.4 and tokenizers) require a Rust compiler. If you see error: can't find Rust compiler:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
pip install <package>

On Windows, download and run the Rust installer from https://rustup.rs.

ModuleNotFoundError: No module named 'distutils' on Python 3.12+

If the build log contains this line, the package’s setup.py still imports distutils, which was removed from Python 3.12. Install the compatibility shim:

pip install setuptools

Setuptools 60+ injects a distutils shim back into sys.modules. If the project’s pyproject.toml does not list setuptools under [build-system] requires, you may also need --no-build-isolation for the install to see it. File an issue with the package upstream; they need to migrate off distutils.

Wheel Cache Has a Bad Build From Last Time

pip caches wheels under ~/.cache/pip/wheels (Linux/macOS) or %LOCALAPPDATA%\pip\Cache (Windows). A previous failed-but-partial build can poison the cache so every subsequent install replays the failure. Clear it:

pip cache purge
pip install --no-cache-dir <package>

--no-cache-dir forces a clean download for that single install without affecting future ones.

uv or pip-tools Locked an Old Wheel Tag

If you use uv pip install or pip-tools with a requirements.lock, the lock can pin a wheel from a manylinux generation that no longer exists. Regenerate the lock on the target platform (or use uv lock --upgrade) so the newer manylinux_2_28 tag gets selected. See also Fix: uv not working for related uv troubleshooting.


What Other Tutorials Get Wrong About Wheel Build Failures

Most pip tutorials list the same fixes but frame them in ways that produce subtle bugs.

They miss the “read above the error” rule. “Could not build wheels” is the wrapper, not the cause. The actual compiler / linker / Rust error is 20 to 50 lines above. Articles that focus on the bottom line send readers chasing pip when the bug is in gcc, ld, or rustc output.

They omit the manylinux tag evolution. Old pip cannot see manylinux_2_28 wheels published in 2024. The same pip install numpy that worked on a coworker’s machine fails on yours because their pip is newer. Articles that say “wheels exist for this package” without flagging the pip-version dependency miss the most common silent fallback.

They recommend --user to fix permission issues. --user masks the real problem (no write access to system site-packages) and creates a parallel install location that confuses every subsequent shell session. Use a virtual environment instead; it solves the same problem cleanly.

They miss the Rust requirement for modern packages. cryptography, pydantic-core, tokenizers, polars, and ruff all need rustc to build from source. Articles that show only the C-compiler fixes leave Rust-dependent packages broken with confusing “can’t find Rust” errors.

They confuse Could not build wheels with No matching distribution found. They look similar but have different causes. The former means the build path failed; the latter means no wheel and no buildable source were found at all (often a Python version or platform mismatch). The fixes differ.

They omit Conda / Miniforge as the answer for scientific Python. NumPy, SciPy, TensorFlow, PyTorch, OpenCV all have pre-compiled Conda builds that skip every pip wheel-building problem entirely. Articles that insist on staying in pip for scientific stacks send readers into hours of debugging that Conda would skip.

Frequently Asked Questions

Why does the same package install on my coworker’s machine but fail on mine?

Three common reasons. First, their pip is newer and sees a wheel for your platform that yours does not. Second, they have build tools installed (Xcode CLT on macOS, build-essential on Linux) and you do not. Third, they are on a different architecture (x86_64 vs ARM64) and only one has wheels available. pip install --upgrade pip first; then check build tools.

What is the difference between Could not build wheels and No matching distribution found?

The former means pip found a source distribution and tried to compile it, but the build failed. The latter means pip could not find any installable distribution (no wheel, no source) for your Python version and platform. They have different causes (build tool issue vs metadata mismatch) and different fixes.

Should I use --no-build-isolation?

Only as a last resort. PEP 517 build isolation creates a clean environment for the build, which is good for reproducibility but breaks when build dependencies are in your active environment instead. --no-build-isolation makes the build use your env directly. If you use it, install the build deps from [build-system] requires yourself first.

Why do I need a Rust compiler now?

Since 2021, multiple high-profile Python packages adopted Rust for performance: cryptography 3.4+, pydantic v2 core, tokenizers, polars, ruff. When wheels are not available for your platform, pip falls back to building from source, which requires rustc. Install with curl https://sh.rustup.rs | sh (Linux / macOS) or rustup-init.exe (Windows).

Is --prefer-binary always safe?

Yes, with one caveat: it may pick an older version of a package that has wheels over a newer version that only has source. For most users that is fine; for users who specifically need bleeding-edge features, it can hide that the newer version is missing wheels on their platform. The flag is a useful default for CI and Docker builds.

Why does Conda often succeed when pip fails?

Conda ships pre-compiled binaries from its own channels (defaults, conda-forge) along with all required system libraries. When you conda install numpy, you get numpy plus the BLAS/LAPACK libraries it needs, all linked correctly. pip only ships the Python package and assumes the system libraries are present. For scientific Python especially, Conda’s bundled approach skips the entire build-tool dependency chain.

Related: Fix: ModuleNotFoundError: No module named | Fix: error: externally-managed-environment | Fix: IndentationError: unexpected indent

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles