ImportError: cannot import name 'x' from partially initialized module 'y' (most likely due to a circular import)
Python · updated 2026-11-02
Raised while a module is still executing its top-level code and another module tries to import a name from it that hasn't been defined yet. Python says so in the message: partially initialized module, most likely due to a circular import. It often appears after a refactor, or only for one entry point, because import order decides which side of the loop runs first.
Traceback (most recent call last):
File "/srv/app/main.py", line 3, in <module>
from app.models import Order
File "/srv/app/app/models.py", line 4, in <module>
from app.services import charge_order
File "/srv/app/app/services.py", line 2, in <module>
from app.models import Order
ImportError: cannot import name 'Order' from partially initialized module 'app.models'
(most likely due to a circular import) (/srv/app/app/models.py)Quick fix
Follow the traceback to find the loop (here models imports services, which imports models). Break it by moving the shared definitions into a module both can import, by importing the module rather than the name (from app import models, then models.Order at call time), or by moving one import inside the function that needs it. For type hints only, guard the import with typing.TYPE_CHECKING.
- Read the traceback top to bottom and write down the chain of imports until it returns to a module already in it.
- Decide which dependency is the wrong way round, usually a lower-level module importing a higher-level one.
- Move the shared code (models, constants, types) into a module that imports neither side.
- Or import the module and access attributes at call time, or import inside the function.
- For imports used only in type hints, use if TYPE_CHECKING: and string annotations.
# app/services.py
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING: # only for type checkers, never at runtime
from app.models import Order
def charge_order(order: Order) -> None:
from app import billing # import where it's used, breaks the cycle at load time
billing.charge(order.total)Why it happens
Two modules import names from each other at the top level
models imports a helper from services and services imports a model from models. Whichever loads first is still half-initialized when the other asks for its names.
Imports added only for type hints
Annotating a parameter with a class from another module adds a runtime import that can close a loop, even though nothing runs that code.
A package __init__.py that imports its submodules
Re-exporting everything in __init__.py makes every submodule import the whole package, so any submodule that imports from the package can loop.
A file named like a module it imports
A local file such as email.py or requests.py shadows the real package and can produce the same partially initialized message.
Other fixes
- Keep a one-way layering (models <- services <- api) and let linters like import-linter enforce it.
- Prefer from __future__ import annotations so annotations stay strings at runtime.
- Avoid wildcard re-exports in package __init__.py files.
Same error, other wording
- AttributeError: partially initialized module 'app.models' has no attribute 'Order' (most likely due to a circular import)
- ImportError: cannot import name 'settings' from partially initialized module 'config'
Let your coding agent catch the next one
Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:
> Find the circular-import ImportError in production, read the traceback to name the modules in the loop and the release that introduced it, and propose the smallest change that breaks the cycle.
Related errors
- ModuleNotFoundError: No module named 'x'
- AttributeError: 'NoneType' object has no attribute 'x'
- KeyError: 'email'
References: Python: The import system, Python: typing.TYPE_CHECKING