PythonMastery
reference 4 min read · lesson 38 of 45 in Errors

ImportError: cannot import name 'X' from partially initialized module (most likely due to a circular import)

1 · The lesson

read

What 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

python
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 models behaves differently from from models import User. The plain form binds the module object and looks up the attribute later, when it is needed; the from form 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:

python
# 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:

python
# 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:

python
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:

python
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.py that 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.

See Also