Fix "numpy.dtype size changed" After a NumPy 2 Upgrade
- Cause: a package with compiled extensions was built against NumPy 1.x and you now have NumPy 2. Check with
python -c "import numpy; print(numpy.__version__)". - Best fix: upgrade the package named in the traceback (
pip install -U pandas,scikit-learn,scipy,opencv-python-headless). Every current release I tried imports cleanly on numpy 2.5.3. - If you must keep the old package version:
pip install "numpy<2". That installed 1.26.4 and the old packages imported again. - Doesn't help: reinstalling the same package version with
--force-reinstall --no-cache-dir. It fetches the identical wheel. - Prevent it: install numpy-dependent packages in one
pip installcommand (or one requirements file), then runpip check. Note thatpip checkmissed the OpenCV case below.
You import pandas, scikit-learn, or another numpy-based library and the import dies inside a compiled module:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File ".../site-packages/pandas/__init__.py", line 46, in <module>
from pandas.core.api import (
File ".../site-packages/pandas/core/api.py", line 1, in <module>
from pandas._libs import (
File ".../site-packages/pandas/_libs/__init__.py", line 18, in <module>
from pandas._libs.interval import Interval
File "interval.pyx", line 1, in init pandas._libs.interval
ValueError: numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject
That is pandas 2.1.4 on numpy 2.5.3. The two numbers are the size of numpy's dtype object: 96 bytes is what the extension was compiled with (NumPy 1.x headers), 88 is what the running NumPy 2 reports. In every run I did, the numbers were the same 96 and 88. Packages built a different way fail with different text for the same underlying problem, covered in the table below.
Reproduced: Each Error and the Fix That Worked
Setup: one fresh python3 -m venv per row, pip install --upgrade pip, the old package installed with numpy==1.26.4, then pip install "numpy>=2" (resolved to 2.5.3), then the import. The error column shows the last line printed.
| What I ran | Exact error | Fix that worked |
|---|---|---|
pandas 2.1.4, numpy upgraded to 2.5.3, import pandas | ValueError: numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject | pip install -U pandas (got 3.0.6, numpy stayed 2.5.3, import worked) |
scikit-learn 1.3.2, numpy upgraded to 2.5.3, import sklearn | Same ValueError, raised from sklearn/utils/murmurhash.pyx | pip install "numpy<2" (got 1.26.4, import worked) |
scipy 1.11.4, numpy upgraded to 2.5.3, import scipy.linalg | Message A module that was compiled using NumPy 1.x cannot be run in NumPy 2.5.3 as it may crash., then ImportError: numpy.core.multiarray failed to import | pip install -U scipy (got 1.18.1, scipy.linalg.inv worked) |
opencv-python-headless 4.8.1.78, numpy upgraded to 2.5.3, import cv2 | Same "compiled using NumPy 1.x" message, then AttributeError: _ARRAY_API not found, then ImportError: numpy.core.multiarray failed to import | pip install -U opencv-python-headless (got 5.0.0.93, import worked) |
pandas 2.1.4 broken as above, then pip install --force-reinstall --no-cache-dir --no-deps pandas==2.1.4 | Identical ValueError after the reinstall | None; the same version gives the same wheel. Change pandas or numpy instead. |
pandas 2.1.4 installed, later a separate pip install scipy | pip installed scipy 1.18.1 and numpy 2.5.3; import pandas then gave the same ValueError | Install together: pip install "pandas==2.1.4" "scipy>=1.14" chose scipy 1.17.1 with numpy 1.26.4 and both imported |
Which combinations pip allowed:
- pandas 2.1.4, scikit-learn 1.3.2 and scipy 1.11.4 declare an upper bound on numpy (
numpy<2,numpy<2.0andnumpy<1.28.0). Pip still upgraded numpy when asked. For pandas and scipy it printedERROR: pip's dependency resolver does not currently take into account all the packages that are installedwith the conflicting requirement, and ended withSuccessfully installed numpy-2.5.3. The install "succeeded"; the import failed. - opencv-python-headless 4.8.1.78 has no upper bound. Pip upgraded numpy without any warning and
pip checkprintedNo broken requirements found.whileimport cv2was failing.
The reverse direction is different. Packages built for NumPy 2 are meant to also load on NumPy 1.x. With numpy forced down to 1.26.4 (--no-deps), pandas 3.0.6 and scipy 1.18.1 both imported and computed correctly; scipy only printed UserWarning: A NumPy version >=2.0.0 and <2.8.0 is required for this version of SciPy (detected version 1.26.4). scikit-learn 1.9.1 failed, but with a plain Python error, AttributeError: module 'numpy' has no attribute 'long', not the dtype size error. So "dtype size changed" points at an old package on a new numpy, not the other way around.
What's actually happening
Packages such as pandas, scikit-learn and scipy ship compiled C/Cython extensions. When a wheel is built, it records numpy's C struct layout from the numpy version it was compiled against. Cython checks the size of numpy's dtype type when the module loads, and NumPy 2 changed that layout, so a wheel built against 1.x headers sees 88 bytes where it expected 96 and refuses to load. Extensions built with other tools (scipy's Fortran wrappers, OpenCV's bindings) hit NumPy 2's own import check instead, which prints the "compiled using NumPy 1.x" message.
Neither wheel is damaged; the combination is what fails. The most common ways to end up there:
- You ran
pip install -U numpy(or something that requires numpy 2) without upgrading the older packages that sit on top of it. - Packages were added in separate
pip installcommands, as in the scipy row above. Each command is resolved on its own, and a later one can move numpy under an earlier one. - A requirements file pins an old pandas, scikit-learn or OpenCV but leaves numpy unpinned. For packages without a numpy upper bound, pip will happily pick numpy 2.
Step 1: find the package with the old build
The last frames of the traceback name the compiled module: pandas/_libs/interval.pyx, sklearn/utils/murmurhash.pyx, scipy/linalg/blas.py or cv2/__init__.py in the runs above. Then check the installed versions and what pip thinks of them:
python -c "import numpy; print(numpy.__version__)"
pip list | grep -iE "^(numpy|pandas|scipy|scikit-learn|opencv)"
pip check
For the broken pandas venv, pip check printed:
pandas 2.1.4 has requirement numpy<2,>=1.26.0; python_version >= "3.12", but you have numpy 2.5.3.
pip show pandas is less useful here: its Requires: line lists numpy without any version bounds. To see the actual numpy range a package declares:
python -c "import importlib.metadata as m; print([r for r in m.requires('scipy') if 'numpy' in r])"
For scipy 1.17.1 that printed ['numpy<2.7,>=1.26.4', ...]. If pip check is clean but the import still fails (the OpenCV case), go by the traceback.
Step 2: the fix
Upgrade the package, keep NumPy 2
pip install -U pandas # or scikit-learn, scipy, opencv-python-headless
python -c "import pandas" # confirm
This is the fix to prefer. pandas 3.0.6, scipy 1.18.1 and opencv-python-headless 5.0.0.93 all imported on numpy 2.5.3, and pip check came back clean in the pandas and scipy venvs. Upgrading is a larger version jump in some cases (pandas 2.1 to 3.0, OpenCV 4 to 5), so run your tests afterwards.
Keep the old package, downgrade NumPy
pip install "numpy<2"
On Python 3.12 this installed numpy 1.26.4, and scikit-learn 1.3.2 imported again with pip check clean. Use this when you can't upgrade the package yet, for example to reproduce an old result. Pin it in your requirements so the next install doesn't move numpy back.
What doesn't fix it
pip install --force-reinstall --no-cache-dir pandas==2.1.4 downloads the same wheel that was built against NumPy 1.x, so the error is unchanged. (Without a version, pip install --force-reinstall pandas may "work", but only because it installs a newer pandas.)
Install the stack in one pass
python -m venv .venv --clear
source .venv/bin/activate
pip install -r requirements.txt # one resolution for numpy and everything that uses it
pip check
One resolution lets pip see every numpy bound at once. In the test above, pip install "pandas==2.1.4" "scipy>=1.14" in one command picked scipy 1.17.1 (the newest scipy that still allows numpy 1.26) and numpy 1.26.4, and both imported. The same two packages installed in separate commands left pandas broken.
Docker
Each RUN pip install line is a separate pip command, so it behaves like the separate-install row above: a later layer can upgrade numpy under a package installed in an earlier layer. Put numpy and everything that depends on it in one requirements file and one RUN:
# Avoid: an old pandas in one layer, something needing numpy 2 in a later one
# RUN pip install pandas==2.1.4
# RUN pip install scipy
# Prefer: one resolution, then fail the build if the environment is inconsistent
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt && pip check
pip check in the build turns the pandas, scikit-learn and scipy cases into a build failure. It would not catch the OpenCV case, so a one-line python -c "import cv2" in the build is worth adding if you use it. I didn't run these inside Docker for this article; the behaviour shown is pip's, which is the same in a container.
Conda and pip together
The same rule applies in conda environments: numpy and the packages compiled against it should come from one resolver pass. conda install numpy followed by pip install of an old pandas wheel is two separate resolutions, just like the separate-install row above. If you use conda, install the numeric stack through conda in one command (conda install -c conda-forge numpy pandas scipy scikit-learn). I didn't test conda for this article.
Confirming it's fixed
python -c "import numpy, pandas, scipy.linalg, sklearn; print(numpy.__version__, pandas.__version__)"
pip check
This error is raised at import time (as a ValueError or ImportError), so import each affected package once after the fix. pip check is a useful second signal but, as the OpenCV row shows, not a complete one.