壊れた依存関係の修復 - held packages と unmet dependencies

壊れた依存関係の修復 - 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-upgradeapt-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 後に再実行
前回のインストール中断 電源断 / killapt が途中終了 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 updatedpkg --configure -aapt --fix-broken install の順に試したか
  • [ ] apt-cache policy <依存名> で混在リポジトリ(PPA / 手動 .deb)を確認したか
  • [ ] --force-* で無理やり入れる前に、原因(pinning / 混在)を先に潰したか
  • [ ] 複雑な衝突は aptitude で解決案を確認したか

次に読む