What can I do about ImportError Cannot import name X or AttributeError ... most likely due to a circular import?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
These errors often mean Python started importing a module, got interrupted by another import that pointed back to the first module, and then found the first module only half-initialized. That is a circular import. The fix is usually not a clever import trick, but a cleaner module dependency structure.
What a Circular Import Looks Like
Suppose you have two files:
When Python imports a, it begins executing a.py. That import immediately asks for b, so Python starts executing b.py. But b.py imports a again, and now Python sees a only in its partially initialized state.
That is why you get errors such as:
- '
ImportError: cannot import name ...' - '
AttributeError: partially initialized module ...'
The name may exist eventually, but not yet at the time the cycle occurs.
Why the Error Message Can Be Confusing
The failing name is often not the real problem. You may think:
- the function name is wrong
- the class does not exist
- the package path is broken
But the real issue is timing. The symbol is unavailable because the module never finished initializing before the reverse import asked for it.
That is why the phrase “most likely due to a circular import” is so useful. It points to import order and dependency structure rather than to a missing definition.
The Best Fix: Remove the Cycle
The cleanest solution is to move the shared dependency into a third module.
Bad structure:
Better structure:
Example:
This is the most maintainable fix because it aligns the code with the dependency graph you actually want.
A Temporary Workaround: Local Import
If you cannot refactor immediately, moving the import inside a function can break the cycle.
This works because the import happens later, after module initialization time.
It is a valid tool, but it should be used carefully. Local imports solve timing problems, not architecture problems. If the modules genuinely have the wrong responsibilities, the cycle will likely reappear elsewhere.
Another Common Cause: Name Shadowing
Not every “cannot import name” error is a circular import. A file can accidentally shadow a standard library or third-party module.
For example, if your project contains:
and somewhere else you write:
Python may import your local random.py instead of the standard library module. That can produce very similar symptoms, especially if your local file then imports something that depends on the real module.
So always check for filename collisions before assuming the cycle is the only issue.
Type Hints Can Create Hidden Cycles
Sometimes imports exist only for annotations. In that case, use postponed evaluation or TYPE_CHECKING.
This avoids importing Service at runtime just to satisfy a type hint.
That is a very common fix in larger Python codebases.
How to Debug the Cycle
A practical debugging process is:
- Read the full traceback carefully.
- Identify which file import started the chain.
- Look for the point where module A imports B and B imports A.
- Decide whether the shared code belongs in a third module.
For a quick sanity check, add small prints:
inside the modules temporarily. The import order often becomes obvious immediately.
What Not to Do
Do not fix every circular import by scattering local imports everywhere without understanding the dependency graph. That may unblock one import path while making the project harder to reason about later.
Also avoid huge “utility” modules that collect unrelated code just to break cycles. The right extracted module should represent a real shared responsibility.
Common Pitfalls
One common mistake is treating the missing name as the primary bug instead of asking why the module was only partially initialized.
Another issue is importing modules for type hints at runtime when TYPE_CHECKING or postponed annotations would avoid the cycle.
Developers also sometimes create cycles by placing business logic, data models, and application startup code in modules that import each other too freely.
Finally, check for shadowing. A local file named like a standard or third-party module can mimic circular-import symptoms and waste debugging time.
Summary
- A circular import happens when modules depend on each other during initialization.
- The best fix is usually to refactor shared code into a third module.
- Local imports can break the cycle temporarily, but they are not always the cleanest design.
- '
TYPE_CHECKINGand postponed annotations help when type hints create import cycles.' - Always rule out module shadowing before assuming the issue is only a circular dependency.

