Fixing Broken Packages and PPA Conflicts on Ubuntu: Deep Debugging with aptitude and ppa-purge

Ubuntu tutorial - IT technology blog
Ubuntu tutorial - IT technology blog

I’ve set up Ubuntu Server 22.04 on more than 20 VPS instances, and checking package integrity is always the very first thing I do — before anything else. The reason is simple: once I skipped this step and ran apt upgrade directly on a server that still had old PPAs from Ubuntu 20.04. The result was apt locking up completely, forcing me to restore from a snapshot. Two hours wasted. Not fun at all.

Context: When Broken Packages Happen and Why Generic Fixes Aren’t Enough

apt --fix-broken install is the first thing Google suggests, and it resolves about 60% of simple cases. But when conflicts originate from PPAs — especially unmaintained old PPAs — that command just throws an error and does nothing more. At that point, you need to understand why the conflict is happening before you can think about fixing it.

Three scenarios I encounter most often:

  • PPA conflict after dist-upgrade: Upgrading Ubuntu to a new version, where an old PPA provides a package at a higher version than the official repo but its dependencies don’t match the new system.
  • Half-installed package: A power outage or Ctrl+C mid-install leaves dpkg in a half-installed or half-configured state.
  • Dependency hell: Two different PPAs providing different versions of a shared library like libssl or libc.

With PPA conflicts, plain apt typically just reports:

E: Unable to correct problems, you have held broken packages.

No additional useful information. This is where aptitude steps in.

Installing aptitude and ppa-purge

aptitude is an alternative package manager to apt, capable of analyzing dependency conflicts in greater detail and suggesting multiple resolution options instead of just reporting an error. ppa-purge specializes in removing a PPA and downgrading all packages from that PPA back to their official repo versions — two tasks in a single command.

sudo apt install aptitude
sudo apt install ppa-purge

If apt install itself is stuck due to broken packages, try resetting dpkg first:

sudo dpkg --configure -a
sudo apt-get install -f
sudo apt install aptitude ppa-purge

Debugging with aptitude: Finding the Root Cause of Conflicts

Viewing the Full Dependency Tree and Resolution Suggestions

Say you see an error related to python3-apt. Instead of using apt, run it with aptitude:

sudo aptitude install python3-apt

Unlike apt, aptitude doesn’t stop at “cannot install” — it shows multiple options, for example:

The following actions will resolve these dependencies:

     Keep the following packages at their current version:
1)     python3-apt [0.9.3.5ubuntu3 (now)]

     Downgrade the following packages:
2)     python3 [3.11.2-1 (now, ppa:deadsnakes/ppa) -> 3.10.12-1 (jammy)]

Here aptitude is suggesting two paths: keep python3-apt at its current version, or downgrade python3 from the PPA to the official version. This is critical information that apt doesn’t provide — you now know exactly which package from which PPA is causing the problem.

Using the aptitude TUI to Navigate Multiple Conflicts

When the system has multiple conflicts at once, the aptitude TUI lets you inspect them one by one:

sudo aptitude

In the TUI: press U to update the package list, u to view upgrade candidates — aptitude will highlight conflicts and let you choose a resolution with the e key (examine solution). Press q to quit.

Or use the -f flag to have aptitude automatically select the least destructive option:

sudo aptitude -f install

Checking for Held Packages

One commonly overlooked cause: packages being “held” — meaning their version is pinned. When a package is held, apt refuses to upgrade it or install new dependencies for it, causing a broken chain.

# List all currently held packages
apt-mark showhold

# Remove a hold if needed
sudo apt-mark unhold <package-name>

Resolving PPA Conflicts with ppa-purge

Identifying Which PPA Is Causing the Conflict

List all PPAs currently active on the system:

# View source files in sources.list.d
ls /etc/apt/sources.list.d/

# Filter only PPAs from Launchpad
grep -r "^deb " /etc/apt/sources.list /etc/apt/sources.list.d/ | grep "ppa.launchpad"

To find out which PPA a specific package is installed from instead of the official repo:

apt-cache policy <package-name>

The output will be clear:

python3:
  Installed: 3.11.2-1~ubuntu22.04
  Candidate: 3.11.2-1~ubuntu22.04
  Version table:
 *** 3.11.2-1~ubuntu22.04 500
        500 http://ppa.launchpad.net/deadsnakes/ppa/ubuntu jammy/main amd64
    3.10.12-1 500
        500 http://archive.ubuntu.com/ubuntu jammy/main amd64

Immediately clear: python3 is using a version from the deadsnakes/ppa PPA, not the official Ubuntu repo.

Purging the PPA and Downgrading to the Official Version

Once you know which PPA is causing the issue, ppa-purge handles two things at once: removes the PPA from sources.list.d and downgrades all packages from that PPA back to their versions in the official repo.

sudo ppa-purge ppa:deadsnakes/ppa

After ppa-purge completes:

sudo apt update
sudo apt upgrade
sudo apt --fix-broken install

I often run into cases where even after ppa-purge, a few packages are still floating at the wrong version. Check with:

# Find packages not from the official Ubuntu repo
apt list --installed 2>/dev/null | grep -v "/ubuntu " | grep -v "Listing"

Manual Downgrade When ppa-purge Isn’t Enough

Sometimes a PPA has been removed from Launchpad but the packages are still on the machine. ppa-purge can’t handle this case because there’s no longer a remote source to compare against. You’ll need to downgrade manually:

# View available versions of a package in the current repos
apt-cache showpkg <package-name>

# Or use madison for a cleaner view
apt-cache madison <package-name>

# Downgrade to a specific version
sudo apt install <package-name>=<version>

Verification and Monitoring After the Fix

Verifying a Clean System

# Check dpkg for broken packages
sudo dpkg --audit

# Check apt database consistency
sudo apt-get check

# Full upgrade to ensure everything is in sync
sudo apt update && sudo apt full-upgrade

If dpkg --audit produces no output → no broken packages remain. If apt-get check returns exit code 0 → the apt database is consistent. These are the two checks I always run before declaring the job done.

Periodic Health Check Script

After fixing things, I add a quick check script to cron to catch issues early before they become serious problems:

#!/bin/bash
# /usr/local/bin/check-pkg-health.sh

echo "=== Package Health Check: $(date) ==="

echo "--- Broken/unconfigured packages ---"
sudo dpkg --audit

echo "--- Packages from non-official sources ---"
apt list --installed 2>/dev/null | grep -v "/ubuntu " | grep -v "Listing" | head -20

echo "--- Held packages ---"
apt-mark showhold

echo "--- Active PPAs ---"
ls /etc/apt/sources.list.d/ | grep -v ".save" | grep -v ".bak"
# Make it executable
sudo chmod +x /usr/local/bin/check-pkg-health.sh

# Run weekly on Monday morning
sudo crontab -e
# Add the line:
0 9 * * 1 /usr/local/bin/check-pkg-health.sh >> /var/log/pkg-health.log 2>&1

Quick Fixes for Common Errors

Error: “dpkg was interrupted, you must manually run…”

sudo dpkg --configure -a

Error: “The following packages have unmet dependencies”

sudo aptitude install <package>   # aptitude will suggest specific resolution options

Error: apt is completely locked (usually because a previous process was killed)

sudo rm /var/lib/dpkg/lock-frontend
sudo rm /var/lib/apt/lists/lock
sudo dpkg --configure -a

The lesson I’ve taken away from debugging situations like this over and over: don’t add PPAs carelessly. Every PPA is a package source outside of Ubuntu’s control, and it can conflict with system packages at any time — especially after a dist-upgrade. If you need a newer version of software, consider using snap, flatpak, or a binary release directly from the vendor instead of a PPA. At the very least, document every PPA you add to a server and why — your future self will thank you for it.

Share: