Back to Blog
·VerseBlocks

Dataverse cannot delete dependency errors, and how to find what's actually blocking you

DataversePower PlatformAdministrationTroubleshooting

Someone tries to delete a column nobody seems to use, and Dataverse refuses with a dependency error. The instinct is to check the obvious place, the form the column sits on, remove it there, and try again. Sometimes that's the whole fix. More often the real block sits two or three components away from the column itself, a workflow step built against it years ago, a view a dashboard chart happens to be built on, something nobody thought to check before opening a ticket.

You can't delete a solution component that has dependencies from another solution component. That's simply how the framework works. Dependencies are records the solutions framework creates automatically, specifically so a required component can't disappear while something still points at it. The textbook Microsoft example is a field a form needs, try to delete that field and the delete stops, because removing it would break the form. The column sitting in front of you is very possibly the same case, just with more steps between it and the thing you'd actually notice breaking.

The dependency types that decide whether a delete goes through

Dataverse tracks dependencies as one of three values on the Dependency table itself, plus a fourth category recorded somewhere else entirely.

Solution Internal is the one you never see coming, because Dataverse manages it rather than you creating it. It exists when a component simply can't exist without another, the way a table's relationships, forms, views and attributes can't be separated from the table itself, which is why deleting a table takes all of them with it automatically. For the full range of things that live under a table this way, there's a longer breakdown of what actually runs on a Dataverse table.

Published is the type that shows up most when a maker tries to delete a column. It's created when two components are related and then published, a column placed on a form, a picklist attribute pointed at a global option set, a security role granted access to a specific form. Removing it means undoing the association first, in the form designer or wherever the relationship was built, then publishing again before Dataverse allows the delete.

Unpublished applies to the draft version of a component that's mid-edit. Add a column to a form and save without publishing, and the dependency exists as Unpublished until the form is published, at which point it becomes Published instead. It's a transitional state more than a separate category of blocker, though it can throw off troubleshooting against a solution someone left half published.

Invalid works differently from the other three, and isn't stored on the Dependency table at all. It lives in InvalidDependency, and records the opposite kind of problem, a component referencing something required that's missing entirely.

How to actually see what's blocking a delete

Two documented routes sit inside the UI itself. Select a component inside a solution and choose Show Dependencies, either on the solution page directly or under Advanced, and Dataverse opens a Dependencies page split into three tabs, Delete blocked by, Used by, and Uses. Delete blocked by answers the real question, it lists, grouped by solution name, exactly what's standing between you and the delete. Used by and Uses cover the wider picture, everything the component touches in either direction, which matters more for gauging the size of a change than for getting past one block.

Behind that page sits the Dependency table, worth knowing even if you never query it directly. It's read-only, keyed on DependencyId, and each record pairs a DependencyType with two component references. DependentComponentType and DependentComponentObjectId identify the thing that depends, RequiredComponentType and RequiredComponentObjectId identify the thing it depends on, and VersionNumber sits alongside them. When the UI's grouping by solution name isn't specific enough, this is what's actually underneath it.

InvalidDependency is the table to check when a dependency exists but doesn't point at anything real. Its one writable column is MissingComponentId, the identifier of whatever's absent. Its read-only columns, ExistingComponentId, ExistingComponentType, MissingComponentType and MissingComponentInfo among them, tell you which component made the reference and what it expected to find.

The programmatic routes, and which one answers the real question

Five SDK messages cover this territory, and it's easy to reach for the wrong one. RetrieveDependentComponentsRequest, matched in the Web API by the RetrieveDependentComponents function, returns the components that directly depend on the one you're checking. RetrieveRequiredComponentsRequest, with its RetrieveRequiredComponents function, runs the same check in reverse, what the component depends on rather than what depends on it. Both describe the wider relationship graph. Neither answers the delete question on its own.

RetrieveDependenciesForDeleteRequest, and its Web API function RetrieveDependenciesForDelete, is the one built for the question people actually have. It returns every dependency that could prevent a component being deleted, the same information the Delete blocked by tab shows, without needing a browser open. Call it before attempting a delete on anything in a solution you didn't build, and the answer comes back directly instead of surfacing as an error afterwards. RetrieveDependenciesForUninstallRequest and its RetrieveDependenciesForUninstall function do the equivalent work one level up, at the managed solution rather than the component, while RetrieveMissingDependenciesRequest, with its RetrieveMissingDependencies function, runs before export instead of delete, checking which components outside the solution the target environment needs to already have.

The same calculation runs in reverse too, when you add a component rather than remove one. AddSolutionComponentRequest takes ComponentType and ComponentId, and adding an existing table like Account pulls in the components it requires, relationships, forms and views among them, based on the same dependency calculation, unless DoNotIncludeSubcomponents is set to stop it. Microsoft doesn't document an exact UI label called add required objects, but that's the behaviour makers mean when they use the phrase, the dependency framework deciding on its own what has to travel with the component you asked for.

When the block reaches a user's screen, the wording is specific enough to search for directly. Uninstall a solution that another solution still needs, and the message reads "This solution cannot be uninstalled because the [Component Type] with id Component Id is required by the [Solution B] solution." A circular dependency between two solutions produces a blunter version, "Failed deleting solution . Solution dependencies exist, cannot uninstall." And when the real cause is something like an option set with a default value that a workflow in another solution depends on, the older error text reads "Cannot Delete Component Cannot delete Solution because one ore more components require it.", typo and all, apparently never corrected.

Missing dependencies when you import a solution

Import failures are a different flavour of the same underlying check. Bring a solution into an environment missing something it depends on, a table, a column, a form, another solution's component, and the process stops with "Import failed due to missing dependencies." The reference exists in the source environment and doesn't exist in the target, and Dataverse won't proceed on a guess.

The Show dependencies button on that import screen opens a Missing dependencies page sorting everything into four groups, Applications, Managed Solutions, Unmanaged Components, and Deprecated Applications. If every item on the list turns out to be a first party Dynamics 365 application, selecting Deploy Dependencies before import lets Dataverse install or update them itself instead of you tracking each one down separately.

There's a second way to check, slower but sometimes more useful. Extract the solution, open solution.xml, and look for the MissingDependencies element inside it, which lists every missing component the import process found, the same information as the page in a format you can search and diff properly. And if you're the one exporting rather than importing, Dataverse already warns you at that point if anything you left out would cause the same failure elsewhere, so the problem is often visible before it ever reaches an import screen.

What Dataverse doesn't track, and why a clean check isn't proof of anything

Everything covered so far is tracked, calculated automatically, and surfaced through Show Dependencies or RetrieveDependenciesForDelete once you know where to look. None of it covers web resources referencing each other by relative link. An HTML web resource that pulls in a CSS or JavaScript web resource through a relative URL creates no tracked dependency at all, according to Microsoft's own documentation on web resource dependencies. Delete that CSS file, and nothing in the dependency framework will have warned you, because as far as Dataverse's tracking is concerned, no relationship between the two ever existed.

The same gap covers references written directly in code. An HTML, CSS or JavaScript web resource can call another web resource from inside its own logic, and that reference isn't picked up by the dependency system either. Even a Silverlight web resource shown outside a table form or chart, which needs an HTML web resource to host it, has that hosting relationship go untracked. Load order fares no better, web resources load asynchronously and in parallel, Dataverse gives no control over the sequence, and whatever ordering the code assumes isn't recorded as a dependency anywhere.

There's one documented way round the relative link problem. Use the $webresource: directive instead of a plain relative URL, and the reference becomes a tracked dependency like everything else. It changes the result of a dependency check rather than only the tidiness of the code, and it's the only fix Microsoft actually documents for this particular gap.

None of the tracked dependency types, and neither of the two documented ways to check them, covers this gap. A clean result from Show Dependencies, or an empty list back from RetrieveDependenciesForDelete, only confirms that nothing tracked references the component about to go. Untracked references, the relative links and in-code calls covered above, never show up in that result at all. A script referenced by relative link from three other web resources will pass every dependency check Dataverse runs and still break three things the moment it's gone.

For anyone about to delete something in an environment with years of accumulated solutions, that split is the discipline worth keeping. Run RetrieveDependenciesForDelete or check Delete blocked by first, because it catches most of what would otherwise stand in the way, and it saves the trouble of finding it by hand. Then, for anything touching web resources specifically, search the code for relative references, because the tracked check has nothing to say about that category at all. In a large environment, a tool like Cartographer, which crawls the metadata inside your own tenant and builds a plain inventory of what components exist and where, is a reasonable place to start before touching anything at all.