Circular Unit Reference
Delphi Programming Language
Severity: ModerateWhat Does This Error Mean?
A circular unit reference means Unit A uses Unit B, and Unit B also uses Unit A — forming a loop that the compiler cannot resolve. Delphi's compiler processes each unit once and cannot handle mutual dependencies in the interface section. The fix is to break the cycle by moving shared declarations to a third unit or by moving one of the uses references to the implementation section.
Affected Models
- Delphi 10.4 Sydney
- Delphi 11 Alexandria
- Delphi 12 Athens
- Embarcadero RAD Studio
- Free Pascal / Lazarus
Common Causes
- Unit A's interface section uses Unit B, and Unit B's interface section uses Unit A
- A chain of three or more units forming a cycle: A uses B, B uses C, C uses A
- Two forms placed in each other's uses lists because each needs to reference the other's type
- A utility unit placed in uses of a base unit, but the utility unit also uses the base unit
- Auto-generated uses clauses in the IDE adding form units that create unintended cycles
How to Fix It
-
Find the cycle. The compiler error message names one of the units in the cycle. Open that unit and look at its interface uses clause — find which unit creates the loop.
The error 'Circular unit reference to UnitName' names the unit that the compiler first detected closing the cycle.
-
Move the uses reference to the implementation section. If Unit B only needs Unit A for its implementation (not its interface declarations), move 'UnitA' from the interface uses to the implementation uses.
A unit can use another unit in its implementation section even if there is a cycle — the cycle restriction only applies to interface sections.
-
Create a shared types unit. If both units need the same types, extract those types into a new unit (e.g., SharedTypes.pas) that neither unit owns but both can use.
This is the architecturally clean solution. The shared unit has no dependencies on either of the original units.
-
For two forms that reference each other, use late binding: instead of declaring a variable of TFormB in FormA's interface, use a string or ID to communicate, or acquire the reference at runtime using Application.FindComponent.
Forms should rarely depend on each other's types. Consider using an event or callback mechanism instead of direct cross-form type references.
-
Use forward declarations where possible. For interface references, you can often declare just the interface name in a shared unit without pulling in the full implementation unit.
Interfaces and abstract classes in a shared unit break cycles by providing a type that both units can reference without depending on each other.
When to Call a Professional
Circular unit references are a compile-time error — the project will not build until the cycle is broken. The best long-term fix is to restructure the architecture so dependencies flow in one direction. A common quick fix is to move one of the conflicting uses entries to the implementation section, which Delphi allows.
Frequently Asked Questions
Why does Delphi allow circular references in the implementation section but not the interface section?
The interface section of a unit defines public types, functions, and variables — things other units depend on. Delphi must fully compile the interface before any other unit can use it. A circular interface dependency means neither unit can be compiled first. The implementation section is compiled after all interface sections are resolved, so a reference there does not create a blocking cycle.
Can I have Unit A use Unit B in the interface, and Unit B use Unit A in the implementation?
Yes, this is a valid and common solution. Unit B uses Unit A only in its implementation — so when Delphi compiles Unit B's interface, it does not need Unit A yet. By the time Unit B's implementation is compiled, Unit A's interface is already compiled and available.
Is this a Delphi-specific limitation or common to all languages?
Most compiled languages have some form of this restriction. Delphi's unit system is modular and compiled in a defined order, so true interface-level mutual dependencies are not possible. C++ header files have the same issue and use forward declarations as the solution. The cleanest solution in any language is the same: avoid mutual dependencies through good architecture.