PythonMastery
reference 4 min read · lesson 38 of 60 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 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

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 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 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. This is often the smallest fix, because Python looks up the attribute later, when the code runs:

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

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

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

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 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.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 breaks when a new import is added at the top of an existing file, because that import closes a loop.

See Also

Found this useful? Share it with a friend who’s learning Python.