skip to content

When a package manager plans an install or an upgrade on a Debian- or RPM-based Linux host, what problem is its dependency solver actually solving, and what does it mean when it reports unmet or broken dependencies?

level: middleimportance: must knowfreq 55%

answer

  1. packages declare names, not contents
  2. it is a satisfiability problem
  3. provides creates virtual names
  4. unmet is about the visible universe
  5. broken is about local database state

basics

~20 s

The solver searches for a set of package versions that satisfies every declared relationship — requires, conflicts, obsoletes, provides — given what is installed plus everything the enabled repositories offer. Unmet means no such set exists; broken means the local database is already inconsistent.

solid answer

~50 s

Packages declare relationships, not contents: `Depends`, `Pre-Depends`, `Conflicts`, `Breaks`, `Replaces` and `Provides` on the Debian side; `Requires`, `Conflicts`, `Obsoletes` and `Provides` on the RPM side, where most requirements are auto-generated at build time from linked library sonames. The solver's job is to pick one version of each relevant package such that every requirement is satisfied and no conflict is violated — a boolean satisfiability problem, which is why `dnf` and `zypper` use the libsolv SAT solver. Two very different failures get conflated. **Unmet dependencies** means the solver found no valid solution in the universe it can see: usually a repository is missing or disabled, or two repositories are being mixed, or a held package pins the graph. **Broken** usually means the *local* state is already inconsistent — files unpacked but never configured after a failed maintainer script. The first is fixed by changing what the solver can see; the second by finishing or unwinding the interrupted transaction.

code

bash · 11 lines
bash
cat > /tmp/control <<'EOF'
Package: myapp
Version: 2.0-1
Architecture: amd64
Depends: libc6 (>= 2.34), libssl3 (>= 3.0.0)
Recommends: myapp-docs
Provides: myapp-server
Conflicts: myapp-legacy
Replaces: myapp-legacy
EOF
cat /tmp/control

go deeper

for a junior

Know that packages declare the names and version ranges they need, and that the package manager searches the enabled repositories for a combination that satisfies all of them before installing anything.

for a middle

Explain the relationship fields on both sides — requires, conflicts, obsoletes, provides — and that solving them is a satisfiability problem, which is why several valid plans may exist and why failures print long constraint chains.

for a senior

Diagnose from the error: decide whether the needed package is invisible to the host, a hold or priority you set is forbidding the solution, or a previous transaction left packages unpacked but unconfigured — and never force past it.

for a principal

Own the policy that prevents these incidents: which repositories a fleet may see, their relative priorities, whether third-party repositories may shadow distribution packages, and whether hosts track a frozen snapshot so every machine solves against the same universe.

## What a package actually declares A package does not carry its dependencies; it carries *assertions about names*. Two families of assertion matter. **Requirements.** Debian's `Depends` means "this must be installed and configured before I am configured"; `Pre-Depends` is stronger and constrains unpack ordering too. RPM's `Requires` is the equivalent. Both accept version ranges: `libssl3 (>= 3.0.0)`. **Negative relationships.** Debian's `Conflicts` means the two packages cannot be installed together; `Breaks` is a weaker statement that a specific version of another package stops working; `Replaces` declares that this package takes over files or the role of another. RPM has `Conflicts`, and `Obsoletes`, which means "when upgrading, remove that package and take its place" — the mechanism behind package renames. **Virtual names.** Both ecosystems have `Provides`: a package may declare that it provides `mail-transport-agent` or `webserver`, and other packages may depend on that abstract name. Several real packages can provide the same virtual name, so the solver may have a genuine choice to make. RPM pushes this further with **automatically generated dependencies**. At build time the tooling inspects the binaries and emits requirements on shared-library sonames with symbol versions, such as `libc.so.6(GLIBC_2.34)(64bit)`, and matching `Provides` on the packages that ship those libraries. Debian achieves comparable granularity through `shlibs`/`symbols` metadata that turns a link-time dependency into a versioned `Depends`. This is why upgrading a core library can cascade across hundreds of packages: the graph really is that dense. ## The solve Given the currently installed set, the set of packages available in every enabled repository, and your request, the solver must choose an assignment — install, upgrade, downgrade, remove, or leave alone, for each package — such that all positive requirements hold and no negative relationship is violated. That is boolean satisfiability. It is NP-hard in general, and real distributions have tens of thousands of packages, so solvers use dedicated engines: `dnf` and `zypper` both build on **libsolv**, a SAT solver specialised for this problem, while APT uses its own resolver. Practical consequences follow: - There may be **several valid solutions**, and the solver applies policy — prefer the newest version, prefer keeping what is installed, prefer the higher-priority repository — to choose among them. - The solver may propose actions you did not ask for: removing a package because something else `Obsoletes` it, or downgrading, or pulling in a large dependency chain. That is not a bug; it is the only assignment it found. - Failure messages can be long, because explaining *why* no solution exists means quoting the chain of constraints that collide. ``` # The shape of an unsatisfiable report # nothing provides libfoo.so.2()(64bit) needed by myapp-2.0-1.x86_64 ``` ## Unmet vs broken These are different states and the fixes differ. **Unmet / unsatisfiable** is a statement about the *universe the solver can see*. The dependency exists somewhere in the world but not in any enabled repository. Common causes: - a required repository was never enabled, or its metadata is stale, - packages from two different distribution releases or two third-party repositories are being mixed, so their library versions cannot coexist, - a package has been pinned or held at a version that blocks the rest of the graph, - the package was built for a different release than the host runs. The fix is always to change what the solver can see, or to relax the constraint you imposed — never to force the install. **Broken local state** is a statement about the *host's database*. On Debian systems, a maintainer script that exits non-zero leaves a package unpacked but unconfigured; further transactions then refuse to proceed until that is resolved. The files may be present while the package is, formally, not installed. Recovery means completing the configuration step or removing the offending package, after fixing whatever made the script fail — often a missing user, a full filesystem, or a service that would not start. ## Conflicts you meet in practice - **File conflicts.** Two packages ship the same path. The low-level installer detects this at unpack time and aborts, because file ownership must be unique. Legitimate cases are declared with `Replaces`/`Obsoletes`; undeclared ones mean two repositories are shipping overlapping builds of the same software. - **Mixing releases.** Pulling a single newer package from a newer suite drags in a newer libc, which every other package's constraints then reject. The graph is release-wide; there is no such thing as upgrading one package "in isolation" once shared libraries are involved. - **Third-party repositories that shadow distribution packages.** When a vendor repository ships its own build of a common library at a different version, the solver may be forced into a choice that quietly replaces distribution packages. Repository priorities exist to prevent that. ## How to reason about a failure Read the error as a constraint chain and identify which of three things is true: the needed thing is not available anywhere the host can see; something you configured (a hold, a pin, an exclude, a priority) is forbidding the solution; or the local database is already inconsistent from a previous failed transaction. Nearly every real incident is one of those three, and forcing past the error converts a solvable planning problem into a corrupted inventory.

  • What is a virtual package and why does it complicate the solve?
    A virtual package is a name declared through `Provides` that no single real package owns — `mail-transport-agent`, for example. Several real packages may provide it, so a dependency on that name gives the solver a genuine choice, and the choice is resolved by policy: what is already installed, repository priority, or an explicit default. It is also how a package rename stays compatible.
  • Why does mixing packages from two distribution releases usually fail?
    Because shared-library dependencies are release-wide. A binary from a newer release requires a newer glibc or OpenSSL soname, and installing that library version conflicts with the constraints of every package built against the older one. The solver either refuses or proposes replacing large parts of the system — which is why pulling one package from a newer suite is a known way to wreck a host.
  • An upgrade proposes removing a package you never mentioned. How do you interpret that?
    Read it as the solver reporting the only assignment it found. Usually another package declares `Obsoletes` on it, or the newer version of a dependency conflicts with it, or the package is no longer available in any enabled repository so keeping it would violate a constraint. Investigate which relationship forces the removal before accepting the transaction — the answer is in the constraint chain, not in the package you asked about.

saying these in an interview costs you the question

  • Thinking the solver just installs whatever the package lists
  • Confusing an unsatisfiable plan with a corrupted local database
  • Forcing an install to get past a dependency error
  • Believing one package can be upgraded independently of shared libraries
  • Assuming Recommends and Depends are enforced identically

context