壊れた依存関係の修復 - held packages と unmet dependencies
「held packages」と「broken dependencies」は何が違うのか?
結論: 両者は別問題。held(保留) はパッケージが意図的に更新対象から外れている状態で、エラーではない。broken dependencies(壊れた依存関係) は必要な依存が満たせず、インストール・更新が完了できない異常状態。前者は
apt-mark、後者はapt --fix-broken installが対処の起点になる。
apt を使っていると、次のような違う症状を「依存関係の問題」とまとめて捉えがちだが、原因も対処も異なる。
# (A) held: 更新が保留されただけ。エラーではない $ sudo apt upgrade The following packages have been kept back: linux-generic nvidia-driver-535
# (B) broken: 依存が満たせず処理が止まる。これは異常 $ sudo apt install some-package The following packages have unmet dependencies: some-package : Depends: libfoo (>= 2.0) but 1.8 is to be installed E: Unable to correct problems, you have held broken packages.
(A) は「apt がわざと更新を見送った」状態で、システムは正常。(B) は「必要なライブラリのバージョンが噛み合わない」状態で、放置すると他のインストールも巻き込まれる。まずどちらの症状かを切り分けるのが最初の一歩だ。
前提(対象環境)
- OS: Ubuntu / Debian 系(apt / dpkg を使うディストリビューション)
sudoが使える前提で進める- サードパーティ PPA や手動
.debを入れた直後に (B) が起きやすい
なぜパッケージが「kept back(保留)」されるのか?
結論:
apt upgradeは新規パッケージの追加や既存パッケージの削除を伴う更新を拒否する保守的な設計。そのため依存が増えるカーネルやメタパッケージは「kept back」になる。Ubuntu の phased updates(段階的配信) でも一時的に保留される。
apt upgrade で「kept back」が出る理由は主に 2 つある。
| 理由 | 起きやすいパッケージ | 解消方法 |
|---|---|---|
| 更新に新規依存の追加が必要 | カーネル(linux-generic 等) |
apt full-upgrade |
| 更新に既存削除が必要 | メタパッケージ / 移行パッケージ | apt full-upgrade |
| phased updates で配信途中 | 任意(Ubuntu のみ) | 待つ / 後述の確認 |
最も多いのは 1 つ目だ。apt upgrade は安全側に倒すため、「今あるものを新しくする」ことはしても「新しいパッケージを増やす」「いらなくなったものを消す」ことはしない。カーネル更新は新しい ABI バージョンのパッケージ追加を伴うため、この制約に引っかかって kept back になる。
新規追加・削除も許可してまとめて更新するには full-upgrade(apt-get では dist-upgrade)を使う。
$ sudo apt full-upgrade
Ubuntu の phased updates は、更新を全ユーザーに一斉配信せず数日かけて段階展開する仕組み。自分の端末がまだ対象%に入っていないと、その更新だけ kept back になる。この場合は数日待てば自然に降ってくる。今すぐ確認したいなら次で実体を見る。
$ apt-cache policy <パッケージ名>
明示的に hold されたパッケージをどう確認・解除するのか?
結論: 誰か(または自分)が
apt-mark holdで意図的に更新を止めているケースがある。apt-mark showholdで一覧を確認し、不要ならapt-mark unhold <pkg>で解除する。dpkg --get-selectionsでも確認できる。
full-upgrade でも更新されないパッケージがあるなら、明示的に hold(固定) されている可能性が高い。hold は「このパッケージは更新するな」という指定で、特定バージョンに固定したいとき(カーネル・ドライバ等)に使われる。
まず hold 中のパッケージを一覧する。
$ apt-mark showhold
nvidia-driver-535 linux-image-generic
ここに出た名前が、更新対象から外されているパッケージだ。同じことは dpkg でも確認できる。
$ dpkg --get-selections | grep hold
nvidia-driver-535 hold
固定の必要がなくなったら hold を解除する。解除すれば次の upgrade から通常どおり更新対象に戻る。
# hold を解除 $ sudo apt-mark unhold nvidia-driver-535 # 逆に固定したいとき $ sudo apt-mark hold nvidia-driver-535
意図的に hold しているパッケージ(動作確認済みのドライバ等)を不用意に unhold すると、次の更新で挙動が変わることがある。なぜ hold されているかが分からないうちは解除しないこと。心当たりがなければ、まず誰がいつ設定したか(チームの運用ルール・プロビジョニングスクリプト)を確認する。
unmet dependencies はなぜ起きるのか?
結論:
unmet dependenciesは「必要なバージョンの依存が入手・両立できない」状態。原因はリポジトリの混在(PPA・手動.deb)、apt update不足、インストールの中断、ピン留め設定の 4 つに集約される。エラー本文のDepends:行が直接の手がかり。
unmet dependencies が出たら、まずエラー本文を読む。どの依存が、どのバージョン制約で満たせないかが書いてある。
The following packages have unmet dependencies:
packageA : Depends: libbar (>= 3.0) but it is not going to be installed
Depends: libbaz (= 1.2) but 1.4 is to be installed
Depends: libbar (>= 3.0) but ... は「libbar の 3.0 以上が要るのに、それが入れられない / 別バージョンが入ろうとしている」という意味だ。原因は次の 4 つに整理できる。
| 原因 | 起きやすい状況 | 対処の方向 |
|---|---|---|
| リポジトリの混在 | PPA / 手動 .deb / 異なる Ubuntu 版を混ぜた |
該当 PPA を外す / --fix-broken |
apt update が古い |
依存の新バージョンをまだ知らない | apt update 後に再実行 |
| 前回のインストール中断 | 電源断 / kill で apt が途中終了 |
dpkg --configure -a |
| apt pinning(優先度設定) | /etc/apt/preferences でバージョン固定 |
ピン設定を見直す |
特に多いのが 1 つ目で、外部 PPA や野良 .deb が公式リポジトリと食い違うバージョンの依存を要求するケース。apt-cache policy で、その依存がどのリポジトリから来ているかを確認できる。
$ apt-cache policy libbar
libbar:
Installed: 2.8-1
Candidate: 2.8-1
Version table:
3.0-1 500 500 https://ppa.example/ubuntu jammy/main amd64 Packages
*** 2.8-1 500 500 http://archive.ubuntu.com/ubuntu jammy/main amd64 Packages
候補(Candidate)と要求バージョンがどのリポジトリ由来かを見れば、混在が原因かどうかが判断できる。
壊れた依存関係をどう修復するのか?
結論: 定番は
sudo apt --fix-broken install(旧apt-get -f install)。半端な依存を解決しようと試みる。中断が原因なら先にdpkg --configure -a。リポジトリ情報が古いだけならapt updateで直ることも多い。
修復は影響の小さい順に試す。いきなりパッケージを強制削除するのではなく、apt 自身に解決させるのが基本だ。
まず、リスト情報を最新化してから壊れた依存の自動修復を試みる。
$ sudo apt update $ sudo apt --fix-broken install
--fix-broken(-f)は、依存関係が壊れたパッケージを検出し、足りない依存の追加や不要なものの削除を提案・実行する。前回の apt が処理途中で止まっていた場合は、先にこちらで「展開済みだが未設定」のパッケージを設定し直す。
$ sudo dpkg --configure -a $ sudo apt --fix-broken install
apt が「このパッケージを削除する」と提案してきたときは、何が消えるのかを必ず読む。意図しない重要パッケージの削除を伴う提案なら、yes と答える前に立ち止まる。
解決策としてよく出回る sudo dpkg -i --force-depends *.deb や --force-all は、依存チェックを無視して無理やり入れるため、その場は通っても後でさらに壊れる。force 系オプションは最終手段であり、原因(混在リポジトリ・pinning)を直す方が先。
復旧の定番セットは次の順で実行する。
sudo apt update sudo dpkg --configure -a sudo apt --fix-broken install sudo apt full-upgrade
aptitude で解決案を出すには?
結論:
apt --fix-broken installで解決できない複雑な依存衝突は、aptitudeが複数の解決案を対話的に提示してくれる。採用したくない提案は拒否でき、apt より柔軟に落としどころを探れる。
apt が「解決できない」と諦める依存衝突でも、aptitude なら段階的な解決案(どれを保留し、どれをダウングレードするか等)を順に提示する。標準では入っていないことが多いので導入する。
$ sudo apt install aptitude $ sudo aptitude install <パッケージ名>
aptitude は依存が満たせないとき、Accept this solution? [Y/n/q/?] のように複数案を提示する。n で次の案、y で採用と、納得できる案を選べる。apt の「全部入れるか諦めるか」より粒度の細かい操作ができるのが利点だ。
aptitude の提案にも「大量のパッケージを削除する案」が混ざることがある。提示された解決策の削除・ダウングロード対象を必ず確認してから採用すること。安易に最初の案を呑むと、apt で force するのと変わらない結果になる。
それでも直らないときのチェックリスト
結論: 症状が held なのか broken なのかを切り分け、broken なら「update → configure → fix-broken」を順に当てる。混在リポジトリの心当たりがあれば、その PPA を外して再評価するのが最短ルート。
- [ ] 症状は「kept back(保留)」か「unmet dependencies(壊れ)」かを切り分けたか
- [ ] kept back なら
apt full-upgradeを試したか(phased updates なら待つ) - [ ]
apt-mark showholdで意図的な hold が無いか確認したか - [ ]
unmet dependenciesのエラー本文のDepends:行を読んだか - [ ]
sudo apt update→dpkg --configure -a→apt --fix-broken installの順に試したか - [ ]
apt-cache policy <依存名>で混在リポジトリ(PPA / 手動 .deb)を確認したか - [ ]
--force-*で無理やり入れる前に、原因(pinning / 混在)を先に潰したか - [ ] 複雑な衝突は
aptitudeで解決案を確認したか