A while ago, one of my experimental servers was stuck at exactly this point: I had to resolve apt package conflicts manually “by hand”. I had a Debian system that had never been fully upgraded over the years, but was stuck somewhere between Stretch and Bookworm—with a wild mix of package versions that really should never coexist on one system. apt refused to do anything, and dpkg --configure -a always failed at the same spot. In the end, no standard command helped anymore. I had to manually unpack the .deb packages with Python just to understand what the package manager was actually trying to install, and which versions were already present on the system.
This exact scenario—a release upgrade that wasn’t completed properly and catches up with you months or years later in the form of cryptic apt errors—is a classic, and that’s what this article is about.
You switched your /etc/apt/sources.list from stretch to bullseye, or from bullseye to bookworm, apt update still worked—but with apt upgrade or apt full-upgrade everything fails with cryptic error messages. This problem occurs much more often than you might think, especially on servers that have been running for a long time, “growing along” over several Debian generations rather than being freshly installed.
Why this Happens
A Debian release upgrade is not a simple version bump. Between two Debian generations, the following change:
- Package names and splitting – one package splits into two, or two packages are merged
- Dependency versions – a package now requires a libc version that’s not installed yet
- Config file formats –
dpkgprompts during the upgrade whether a changed config file should be overwritten and aborts at this very step in non-interactive runs (e.g., via SSH with-y) - Remnants from old repos – if there are lines from the old release left in
sources.list(a typical error: only the main line adjusted, but-updatesor-securityforgotten),aptmixes packages from two generations
The result is usually one of three classes of errors:
trying to overwrite '/usr/lib/xyz', which is also in package abcThe following packages have unmet dependenciesdpkg: error processing package xyz (--configure): dependency problems - leaving unconfigured
Step 1: Take Stock Instead of Panicking
Before you install or remove anything, get a clear overview:
apt list --upgradable 2>/dev/null apt-cache policy | head -20 cat /etc/apt/sources.list /etc/apt/sources.list.d/*.list
Explicitly check whether all lines (main, updates, security, possibly backports) consistently point to the same release. That is the most common cause and can be fixed in 5 minutes—before you dive into more complex error cases which are actually just symptoms of this single error.
Step 2: Get dpkg back into a clean state
If dpkg aborts in the middle of configuration, try this first:
sudo dpkg --configure -a
This tries to finish the configuration of all half-installed packages. If there’s an error for a particular package, note its name—this is your starting point for focused troubleshooting.
Step 3: Let apt fix dependencies automatically
sudo apt --fix-broken install
This is the command that works in most cases: apt itself tries to find a combination of installs and removals which will make the dependency chain consistent again. Important: Before confirming, read the proposed list of changes—sometimes apt will propose removing a package you actually need, because (for now) no compatible version can be found.
Step 4: The “trying to overwrite” Case
This error message means: Two packages want to install the same file, because the package structure has changed between releases (e.g., a package was split into package-common and package-bin). The quick but dangerous solution would be dpkg -i --force-overwrite. Don’t do this as your first step. Instead, first check whether this is a known split/package migration issue:
dpkg -S /path/to/conflicting-file apt-cache policy <affected-package>
Usually, it’s enough to cleanly remove the older, now obsolete package first (apt remove <old-package>) rather than force-installing the new one on top. Forced installations often leave you with inconsistent dpkg status databases, which may come back to haunt you weeks later.
Step 5: If nothing works anymore – Manual .deb analysis
In stubborn cases where apt and dpkg block each other (e.g., due to a mix of two no-longer-supported intermediate versions), it helps to extract and inspect the affected package manually, instead of continuing to trust the package manager:
apt-get download <package-name> dpkg-deb -R <package-name>*.deb /tmp/package-content
This way, you see exactly which files the package wants to deliver, and you can manually check what is already (perhaps with a different name) present on your system. This is the last resort if the regular way with apt --fix-broken install leads to a dead end—for example, when the package database is so broken by a mixture of two or three Debian generations that apt can no longer compute a valid solution path.
Prevention for Next Time
- Always perform release upgrades with
do-release-upgrade(on Ubuntu) or the official Debian upgrade guide, not by manually editingsources.listand then runningdist-upgrade - Before every upgrade, make a snapshot/backup of the system (LVM snapshot, VM snapshot, or at least
dpkg --get-selections > packages-backup.txt) - Rewrite
sources.listfrom scratch after every switch instead of editing line by line—this avoids “forgotten -security line” issues
Conclusion
Most “apt broken after release upgrade” cases are not actual system damage, but inconsistent intermediate states that can be cleanly resolved in over 80% of cases with dpkg --configure -a followed by apt --fix-broken install. Only in the stubborn remainder is it worth resorting to manual .deb analysis—but even then, you have full control over exactly what happens on your system, instead of rolling the dice with --force-overwrite.
With my mailserver case back then, this was ultimately the only way back to a working system: manually extracting each package, comparing, deciding what stays and what must go—instead of blindly trusting the package manager, which simply could not find a valid solution path in this broken intermediate state. The lesson learned was less technical and more organizational: A release upgrade that is done “quickly on the side” will almost always come back to haunt you months later—big time.