Circular Dependencies
Circular dependencies occur when objects reference each other in ways that prevent compilation. This page explains how to identify and resolve them.
What Are Circular Dependencies
The Problem
A circular dependency occurs when:
- Object A references Object B
- Object B references Object A
- Neither can compile without the other
In SOD Context
Common scenarios:
- Page extension needs table extension fields
- Table extension references page elements
- Complex feature interdependencies
Identifying Circular Dependencies
Error Messages
Look for:
- "Circular dependency detected"
- "Cannot resolve dependency"
- Compilation failures mentioning dependencies
Common Patterns
| Pattern | Description |
|---|---|
| Table ↔ Page | Table and page reference each other |
| Feature ↔ Extension | New feature references extended objects |
| Multi-object | Chain of references creates a cycle |
| SOD app ↔ Designer personalization app | A client-side Designer app took a dependency on the SOD app |
| Same App ID under two names | An already-published app effectively depends on itself |
The Designer Personalization App
A CircularDependency deployment failure can arise between a SOD-generated app — typically the second app in Dual-App mode — and a separate extension created by the Business Central Designer (client-side UI personalization).
Why it happens: when a user moves fields in the Designer to sit before or after SOD-generated fields, the personalization app takes a dependency on the SOD app. The error names both apps in the dependency path.
Resolution:
- Uninstall the personalization (Designer) app
- Refresh Symbols with cache clearing
- Retry publishing
Best practice: place SOD-created fields within SOD, not through the Designer, to keep cross-dependencies from forming. Note that on its own this did not prevent the problem in the reported case — the steps above are the fix.
Same App ID Under Two Names
Symptom: deployment fails with Type: CircularDependency, and the dependency path shows two apps that share the same App ID but have different names and different versions. The app depends on itself.
Cause: the Extension Name and/or Start Object Number was changed on a customization that was already published. Reusing the same App ID under a new name, against the app already installed, produces this self-referential dependency.
Prevention: treat changing the Extension Name on a published customization as just as disruptive as changing the Start Object Number. Both require the full procedure described in Object Numbering → "Changing Object Numbers": export the configuration, uninstall the existing extension, delete the existing SOD data, configure the new value, re-create the customizations, and publish again.
The Dual-App Solution
How It Solves the Problem
Dual-App mode:
- Splits objects into two apps
- App 1 (tables) has no dependencies on App 2
- App 2 (pages) depends on App 1
- No circular reference possible
When to Use Dual-App
Use when:
- Single-app compilation fails with circular dependency
- Complex customizations
Enabling Dual-App
Steps
- Open Setup
- Set App Mode to Dual
- Configure Extension Name 2
- Publish
Requirements
- Two unique extension names
- Proper deployment order
See Dual-App Overview for details.
Alternative Solutions
Restructure Customizations
Sometimes circular dependencies can be avoided:
- Rethink the design
- Remove unnecessary references
- Simplify relationships
Evaluate Necessity
Ask:
- Is this reference really needed?
- Can the design be simplified?
- Is there an alternative approach?
Prevention
Design Considerations
When planning customizations:
- Consider dependency direction
- Keep references one-way when possible
- Plan for Dual-App if complexity warrants
Incremental Development
Build incrementally:
- Add objects one at a time
- Publish frequently
- Catch issues early
Troubleshooting
If Dual-App Doesn't Help
Check:
- Are both extension names configured?
- Is the error actually circular dependency?
Persistent Issues
If problems continue:
- Review the specific error
- Identify which objects are involved
- Consider restructuring
- Seek support if needed
How Dependencies End Up in a Generated App
Dependencies are resolved automatically by the compiler — you never add them by hand in SOD. They are written into the generated app's app.json under "dependencies", and Business Central pulls in everything listed there when the app is published.
The most common source of unwanted dependencies
Creating an API or page where all fields on a table are selected. "All fields" can include fields contributed by other installed apps — third-party or ISV extensions — and selecting them makes those apps dependencies of yours.
This is why a generated app can arrive in the target environment demanding an extension you have never deliberately used. See also the note on inherited dependencies in Publishing Failures: if one of your dependencies itself depends on X, you depend on X too.
Dependencies cannot be made individually selectable in SOD (or in Advanced Cloud Security). This was attempted and is not feasible, because there are dependencies between the dependencies.
Removing an unwanted dependency
- Identify the API pages or objects that select all fields on a table where those fields include fields belonging to the unwanted app
- Remove those fields from the table selection / API page — only do this if your app genuinely does not rely on that app's objects or functionality
- Go to Symbols and refresh the symbols list so dependency information is current
- Regenerate the extension
Step 3 matters: if symbols are not refreshed, previously deleted ISV apps can still appear as dependencies in the newly generated app.json. You can verify by opening the .app file in VS Code.
The same behaviour and procedure apply to apps generated by Advanced Cloud Security.
Cleaning up already-installed dependency apps
Publishing a corrected app does not always remove dependency apps that are already installed in the environment. Uninstall and unpublish them directly, via Extension Management or PowerShell.
Consuming SOD Fields from an AL Extension
If you write an AL extension that uses fields created in SOD, add the SOD app as a dependency in your app.json. Three rules decide whether it will publish.
1. The dependency version must match exactly. The version in your app.json must match the version of the SOD app actually installed in the target tenant. A mismatch gives:
AL1024: A package with publisher '...', name '...', and a version compatible with '...' could not be loaded.
Symbols ... could not be found among this tenant's installed apps.
AL0270: The referenced control was not found.
Being able to download symbols in VS Code does not guarantee the installed version matches the one referenced in app.json.
2. A PTE cannot depend on a Dev app. SOD deploys its app as a per-tenant extension. If the app you depend on was published with "Dev" deployment rather than as a PTE, publishing fails with:
AVS0109: The per-tenant extension (or one of its dependencies) cannot be deployed as it has
missing dependencies or the dependencies are conflicting with currently installed apps.
Confirm the dependency app is deployed as a PTE.
3. The dependency must go one way only. Two extensions cannot depend on each other. A circular dependency between your AL extension and the SOD app fails publishing, and can cause the dependency app to uninstall itself during the attempt.
When republishing the SOD app from within SOD for this purpose, use the Microsoft compiler rather than the legacy compiler.
Error code checklist
| Error | Cause |
|---|---|
AL1024 / AL0270 |
Dependency version in app.json does not match the installed SOD app version |
AVS0109 |
The dependency app is deployed as a Dev app while yours is a PTE, or a dependency is missing/conflicting |
| Dependency app uninstalls itself during deploy | Version mismatch or a circular dependency between the two extensions |
Pageextensions are the mechanism for adding actions — a print action, for example — to SOD-generated pages, and any such AL extension needs the dependency declaration above. See Limitations and Graduating from SOD.
Best Practices
Keep It Simple
- Avoid unnecessary complexity
- Use straightforward references
- Don't over-engineer
Plan Ahead
- Consider dependencies when designing
- Test early and often
- Use Dual-App proactively if needed
Document
- Note any circular dependency solutions
- Document why Dual-App was needed
- Help future maintainers understand