ImportError: cannot import name 'X' from partially initialized module (most likely due to a circular import)
1 · The lesson
readWhat this error means
Two modules import each other. Python starts running the first module and reaches its import of the second. It starts running the second module, and that module imports from the first one. But the first module is still only half run, so the name being asked for does not exist yet.
Python tells you this directly. The phrase "partially initialized module" explains the whole problem: the module exists, but it has not finished running. (On Python 3.13, when all the files sit next to the script you run, the same cycle can instead read cannot import name 'User' from 'models' (consider renaming ...). The renaming hint is meant for files that have the same name as a library, so ignore it here. The cause and the fixes are the same.)
When you see it
Traceback (most recent call last): File "app.py", line 1, in <module> from models import User File "models.py", line 2, in <module> from services import send_welcome_email File "services.py", line 1, in <module> from models import User ImportError: cannot import name 'User' from partially initialized module 'models' (most likely due to a circular import) (/app/models.py)
Read that traceback as a loop: app → models → services → models. The last import is where the loop closed.
Why it happens
A Python module runs top to bottom the first time it is imported, and is cached from then on. If models is halfway down its file when services asks it for User, and User is defined below that point, it does not exist yet.
Two details explain most of the confusion:
- Import order changes the outcome. The same cycle can work when entered from one module and fail from another, which is why it often appears after an unrelated file is edited.
import modelsbehaves differently fromfrom models import User. The plain form binds the module object and looks up the attribute later, when it is needed. Thefromform needs the attribute to exist right now.
How to fix it
Import the module, not the name. This is often the smallest fix, because Python looks up the attribute later, when the code runs:
# services.py import models # not: from models import User def send_welcome_email(user_id): user = models.User.get(user_id) # looked up when the function runs, and by then it exists
Move the import inside the function when only one function needs it:
# models.py class User: def welcome(self): from services import send_welcome_email # runs when the function is called, not on import send_welcome_email(self.id)
This is not a hack. It is a normal fix for a real cycle, and the standard library does it in some places.
Better: break the cycle. A cycle usually means the layering is wrong. If models needs services and services needs models, the shared piece belongs somewhere both can import without either importing the other:
models.py <- knows nothing about services services.py <- imports models app.py <- imports both
If you need the import only for type hints, put it under TYPE_CHECKING. It does nothing at runtime:
from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: # never executed at runtime from models import User def send_welcome_email(user: User) -> None: ...
When you'd see this in real code
- Django or SQLAlchemy models import a service that imports the models back. This is the most typical case.
- A
config.pythat imports from the app while the app imports config for its settings. - Test helpers importing the app while the app imports helpers for a fixture.
- Code that worked for months breaks when a new import is added at the top of an existing file, because that import closes a loop.
Related errors
- ImportError: cannot import name 'X' from 'Y': the name really is not there, and there is no cycle.
- ModuleNotFoundError: No module named 'X': the module itself was never found.
- AttributeError: module 'X' has no attribute 'Y': a common symptom of the same cycle, when
import xsucceeded but the attribute is not ready yet.
See Also
- All Python errors: the full index, by type and by when it happens.
- Modules & Imports
- Packaging
Found this useful? Share it with a friend who’s learning Python.