Verified against Claude Code · 2026-07-30
Diagnose and fix a circular import without just moving the problem around
A prompt for tracing exactly which two modules import each other and why, then choosing a real structural fix (restructuring, a local import, a shared interface module) instead of papering over it with a same-file workaround that resurfaces later.
The prompt
Ready to copy — highlighted parts are example details you can swap.
Diagnose this circular import and fix it properly — not by moving an import statement inside a function as a permanent fix, unless that's genuinely the right call and you can say why. ERROR ImportError: cannot import name 'User' from partially initialized module 'app.models.user' (most likely due to a circular import) MODULES INVOLVED app/models/user.py imports from app/services/auth.py at the top; app/services/auth.py imports User from app/models/user.py at the top. WHAT EACH MODULE ACTUALLY NEEDS FROM THE OTHER auth.py only uses User as a type hint on function parameters; user.py never actually calls anything from auth.py at runtime, it was imported for an unused helper. PROJECT SIZE A 40k-line Django monolith with 12 active contributors; restructuring module boundaries needs a real PR, not a quick patch. DIAGNOSTIC PROCESS 1. Trace the exact import chain that closes the loop — module A imports module B at the top level, module B (directly, or transitively through another module) imports something from module A at the top level too. Name the specific line in each file that closes the cycle; "these two modules are circular" without the exact lines is not a diagnosis. 2. Classify why the cycle exists: two modules that genuinely need each other's types (a real design problem), one module importing another only for a type hint (fixable with TYPE_CHECKING and a string annotation, zero runtime cost), or a module importing far more than it actually uses just because it was convenient (fixable by importing only the specific name needed, or not at all). 3. Propose the real fix based on the classification: if it's a genuine mutual dependency, propose either merging the shared pieces into a third module both can depend on downward, or restructuring so the dependency only points one direction — and say which one fits auth.py only uses User as a type hint on function parameters; user.py never actually calls anything from auth.py at runtime, it was imported for an unused helper. better and why. If it's a type-hint-only import, use if TYPE_CHECKING: plus a string-quoted annotation, which resolves the cycle with zero runtime behavior change. Only propose a local import inside a function as the fix if you can name why a structural fix isn't reasonable given A 40k-line Django monolith with 12 active contributors; restructuring module boundaries needs a real PR, not a quick patch. right now — a local import is a valid tool sometimes, but reaching for it first hides a real design issue behind a workaround nobody will remember to revisit. 4. Confirm the fix actually breaks the cycle by tracing the new import graph the same way you traced the old one, not just asserting that it should work. 5. Check whether the same two modules have any other import path between them beyond the one that triggered this specific error — fixing the one line that raised the exception while leaving a second, less obvious circular path intact means the bug resurfaces the next time code execution happens to touch modules in a different order. OUTPUT FORMAT The exact chain that caused the cycle, the classification, the fix with before/after import statements, confirmation the new import graph has no cycle, and a note on whether any other import path between the same two modules was checked and found clear.
Customize
Optional — swap in your own details for the highlighted parts above.
Why this works
Requiring the exact line in each file that closes the cycle, rather than a general description of two modules being circular, matters because Python's real import mechanism is order-dependent and partial: a module is registered in sys.modules the moment its execution starts, not when it finishes, so a cycle only actually breaks at the specific point where one module tries to access a name in the other before that other module has finished running its own top-level code — two files can have a mutual conceptual dependency and never actually crash, or have what looks like a minor dependency and crash immediately, purely based on which specific names are accessed at which specific line, in which order. Classifying the cycle before fixing it separates three genuinely different problems that share the same error message: a real two-way design dependency needs restructuring, an import that's only there for a type hint needs zero runtime change at all (if TYPE_CHECKING guards the import so it never executes, and a string-quoted annotation defers the name resolution), and an over-broad import needs nothing more than narrowing what's actually imported — treating all three the same way, usually by reaching for a local import inside a function, fixes the crash but often hides which of the three was actually true, and a local-import fix on a genuine design problem just relocates the coupling instead of removing it. The project_size field earns its place because the theoretically cleanest fix — extracting shared types into a third module and repointing both original modules downward — is real refactoring work with real review cost, and for a codebase with a dozen active contributors that's a deliberate PR, not something to sneak in as a side effect of fixing an import error; naming the actual size and activity level is what lets the model recommend the local-import workaround honestly, as a stopgap with a named reason, rather than either overreaching into an unrequested refactor or hiding a workaround as if it were the real fix.
Verified against
Claude Code Sonnet 4.6 · 2026-07-30
Changelog
- 2026-07-30 — Initial publish, verified against Claude Code (Sonnet 4.6) on Python 3.12.
Need this built into your business?
If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.
EXPLORE CUSTOM SOFTWARE
