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, reaches its import of the second, starts running that one, and that one imports back into the first — which is still halfway through executing and does not yet have the name being asked for.
Python tells you this outright. The phrase "partially initialized module" is the whole diagnosis: the module exists, it is simply not finished.
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 line is where it 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. Often the smallest fix, because attribute lookup is deferred:
# services.py import models # not: from models import User def send_welcome_email(user_id): user = models.User.get(user_id) # resolved at call time, by which point 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 on call, not on import send_welcome_email(self.id)
This is not a hack — it is a normal answer for a genuine cycle, and it is what the standard library does in 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
For type hints only, import under TYPE_CHECKING — it costs 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 actually see this in real code
- Django or SQLAlchemy models importing a service that imports the models back — the textbook 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 and broke when a new import was added at the top of an existing file, closing a loop that was already nearly there.
Related errors
- ImportError: cannot import name 'X' from 'Y' — the name genuinely is not there, no cycle involved.
- 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.
See Also
- All Python errors — the full index, by type and by when it happens.
- Modules & Imports
- Packaging