AttributeError: module 'x' has no attribute 'y' is a different problem from the ordinary 'X' object has no attribute 'y'. The module was imported successfully; it just doesn’t contain the name you asked for. That almost always means one of these:
- the installed version of the library renamed or removed the name,
- a circular import left the module half-initialized,
- Python imported a different file than the library you meant (a local file with the same name, or the wrong package),
- the name lives in a submodule that wasn’t imported.
For the object version of this error ('NoneType' object has no attribute, 'dict' object has no attribute), see Fix AttributeError in Python.
TLDR
Run this in the program that fails:
print(mod.__file__, mod.__version__, "name" in dir(mod))
- traceback ends in the library’s
__getattr__: name removed/renamed in this version. Fix: use the new name (e.g.np.float_→np.float64) and pin the version. partially initialized module: circular import. Fix: move shared code to a third module.__file__is inside your project: local file shadows the library. Fix: rename it, delete__pycache__.- wrong
__version__or path: another package or venv. Fix:pip show, checksys.executable. module 'xml' has no attribute 'etree': submodule not imported. Fix:import xml.etree.ElementTree.
Start with three prints
Run these from the same program and environment that fails, not from a separate shell:
import numpy # the module from the error
print(numpy.__file__) # which file was actually imported
print(numpy.__version__) # which version (most libraries define this)
print("float_" in dir(numpy))
__file__ answers the shadowing question, __version__ answers the version question, and dir() confirms whether the name exists. Between them they identify the cause in most cases.
The library version changed
This is the most common cause in practice: code written for one major version runs against another, often after a fresh install in CI or Docker picked up the latest release. The traceback ends inside the library’s own __getattr__, which many libraries use to give a better message for removed names:
Traceback (most recent call last):
File "/app/train.py", line 3, in <module>
labels = load_labels([1, 2])
File "/app/data.py", line 4, in load_labels
return np.array(rows, dtype=np.float_)
^^^^^^^^^
File "/app/.venv/lib/python3.13/site-packages/numpy/__init__.py", line 763, in __getattr__
raise AttributeError(
...<3 lines>...
)
AttributeError: `np.float_` was removed in the NumPy 2.0 release. Use `np.float64` instead.
The failing line is in your code (data.py, line 4); the frame below it is the library reporting that the name is gone. When the last frame is a __getattr__ with raise AttributeError(name) from None or similar, it’s the same situation: the library looked the name up for you and it doesn’t exist in this version.
The NumPy names people hit most often:
| Name | Status | Use instead |
|---|---|---|
np.int, np.float, np.object, np.str |
removed in 1.24 | int, float, object, str, or np.int64 / np.float64 |
np.bool |
removed in 1.24, added back in 2.0 | bool or np.bool_ (works in both) |
np.long |
missing in late 1.x, added back in 2.0 as the C long type |
int or np.int64 |
np.float_, np.complex_, np.unicode_ |
removed in 2.0 | np.float64, np.complex128, np.str_ |
np.NaN, np.Inf, np.NAN |
removed in 2.0 | np.nan, np.inf |
np.product, np.cumproduct |
removed in 2.0 | np.prod, np.cumprod |
pandas 2.0 did the same to methods, which shows up as the object form of the error: 'DataFrame' object has no attribute 'append' (use pd.concat([df, other])) and 'DataFrame' object has no attribute 'iteritems' (use .items()).
Fix the code for the version you want to run, then pin that version (numpy>=2,<3 in requirements.txt or pyproject.toml) so CI and production don’t drift. If code works locally but fails in a fresh environment, compare pip freeze from both; the versions differ.
A circular import
When two of your modules import each other, one of them runs while the other is only partly executed. Names defined below the import line don’t exist yet. Python says so directly:
Traceback (most recent call last):
File "/app/main.py", line 1, in <module>
import app.orders
File "/app/app/orders.py", line 1, in <module>
from app import billing
File "/app/app/billing.py", line 4, in <module>
TAX = orders.default_tax()
^^^^^^^^^^^^^^^^^^
AttributeError: partially initialized module 'app.orders' from '/app/app/orders.py' has no attribute 'default_tax' (most likely due to a circular import)
Read the frames top to bottom: main imports orders, whose first line imports billing, and billing uses orders at import time, while orders is still stuck on its line 1 and hasn’t defined default_tax yet. Each in <module> frame is one module in the middle of being imported, so the chain of those frames is the cycle. With from app.orders import default_tax instead of import app.orders, the same situation raises ImportError: cannot import name 'default_tax' from partially initialized module 'app.orders'.
On Python 3.13, if the module is a top-level file next to your script rather than inside a package, the message instead suggests renaming the file. If you know there’s no name collision, treat it as a circular import.
There are two fixes that hold up:
- Move the shared code into a third module that both import, so neither imports the other.
- If the dependency is only needed at call time, move the import into the function that uses it. This works, but it hides the cycle rather than removing it.
If the cycle exists only for type hints, put the import under if typing.TYPE_CHECKING: and quote the annotation.
Python imported a different module
A local file shadows the library
A file named random.py, json.py, requests.py, numpy.py, or test.py in your project (or a folder of that name) is found before the real module. It imports fine but has none of the library’s names. Python 3.13 detects this for the standard library:
AttributeError: module 'random' has no attribute 'randint' (consider renaming '/app/random.py' since it has the same name as the standard library module named 'random' and prevents importing that standard library module)
On older versions you get only the first part. Check module.__file__: if it points into your project instead of the standard library or site-packages, rename your file and delete the stale __pycache__ folder next to it.
The wrong package is installed
Some import names are claimed by more than one PyPI package. The best-known case is module 'attr' has no attribute 's': the attrs library is imported as attr, and an unrelated package literally named attr installs a module with the same name. Fix it with pip uninstall attr followed by pip install --force-reinstall attrs. The same pattern appears with jwt (PyJWT versus the jwt package) and serial (pyserial versus serial). pip show <name> and module.__file__ reveal which one you have.
The program runs in another environment
If __version__ from your shell differs from what the failing program prints, they’re using different interpreters: a different virtualenv, the system Python under cron or systemd, or a Jupyter kernel that isn’t your project’s venv. Print sys.executable in the failing program and install the right version there.
The name is in a submodule
Importing a package doesn’t always import its submodules:
import xml
xml.etree.ElementTree # AttributeError: module 'xml' has no attribute 'etree'
import xml.etree.ElementTree as ET # works
logging.handlers is the same: import logging alone gives module 'logging' has no attribute 'handlers', so it needs its own import logging.handlers. A related mistake is looking in the parent module: os.join doesn’t exist, it’s os.path.join. Sometimes code works by accident because another module imported the submodule first, then breaks when import order changes.
Reading long import tracebacks
Import errors in larger apps (Django startup, pytest collection, Celery workers) produce tracebacks that pass through many framework frames before reaching the import that failed. The frames that matter are your own files, in order; they spell out the import chain. Debugly’s stack trace formatter highlights your own frames and folds the framework ones, which makes a circular import cycle easy to read off a pasted traceback.