Error [externally-managed-environment]

error: externally-managed-environment の直し方(Debian/Ubuntu で pip install が拒否される)

FIX SUMMARY verified
Applies when
debian:12PEP 668 (EXTERNALLY-MANAGED marker on Debian/Ubuntu system Python)pip install into system Python that carries the EXTERNALLY-MANAGED marker

Verified: reproduced in debian:12, then the externally-managed-environment signature was gone after the fix (exit 0).

新しい Debian や Ubuntu で pip install を実行すると、パッケージを入れないまま次のエラーで止まることがあります。

error: externally-managed-environment

error: の行に続けて、This environment is externally managed という説明と、pipx や --break-system-packages の案内が表示されます。)

以前は同じコマンドで入っていたのに、OS を新しくしたり、新しいイメージに移ったりした途端に出るようになります。これは pip の不具合でも、あなたの書き方の誤りでもありません。その Python が「OS のパッケージ管理に任せる領域」だと宣言されていて、pip でそこへ直接入れることを拒否しているためです。

直し方は、仮想環境(venv)を作って、その中に入れることです。急ぎでも、システム全体に入れる回避策より先に、これを検討してください。

なぜ出るのか:PEP 668 と EXTERNALLY-MANAGED マーカー

Debian 12 や Ubuntu 23.04 以降は、OS に付属する Python に EXTERNALLY-MANAGED というマーカーファイルを置いています(/usr/lib/python3.11/EXTERNALLY-MANAGED など)。これは PEP 668 で決められた仕組みで、「この Python は OS のパッケージ管理(apt)が面倒を見ているので、pip で直接いじらないでほしい」という宣言です。

このマーカーがあると、pip はシステム全体への直接インストールを拒否します。理由は、pip が入れたパッケージと apt が管理するパッケージが同じ場所で衝突し、OS のツールが壊れることがあるからです。だから、pip の失敗はあなたを守るための停止で、「入れる場所を分けてください」という案内です。

なぜ手元では出なかったのか

このエラーは、環境によって出たり出なかったりします。マーカーの有無で決まるからです。

  • 公式の Python イメージ(python:3.12-slim など)や、古い OS … マーカーを持たないことが多く、pip install がそのまま通ります。だから「Docker のこのイメージでは入るのに」「前のマシンでは入っていたのに」という感覚になります。
  • 新しい Debian / Ubuntu の system Python … マーカーを持つので拒否されます。

この仕組みは Debian / Ubuntu に限りません。PEP 668 を採用した環境——macOS の Homebrew で入れた Python、Fedora、Arch なども同じマーカーを持ち、同じ externally-managed-environment が出ます。直し方(venv / pipx)も同じです(実測は debian:12。他環境の採用状況は各配布元の情報に拠ります)。

「手元やこれまでの環境では出ないのに、新しい OS や別のベースイメージでだけ出る」という形です。まず、落ちている環境で ls /usr/lib/python3*/EXTERNALLY-MANAGED を見ると、マーカーがあるかどうかを確認できます。

直し方:仮想環境(venv)を使う

いちばん素直な対処は、プロジェクト用の仮想環境を作り、その中に入れることです。venv の中は外部管理のマーカーが無く、pip はその仮想環境を独立した入れ先として扱うので、そのまま通ります。

python3 -m venv .venv
. .venv/bin/activate
pip install requests

Debian / Ubuntu では、venv を作るために python3-venv パッケージが別に要ることがあります。python3 -m venv が失敗するなら、先に入れてください。

sudo apt install python3-venv

仮想環境なら、プロジェクトごとに依存を分けられ、OS の Python にも触れません。activate の代わりに、.venv/bin/pip.venv/bin/python を直接呼んでも同じです。アプリケーション(コマンド)を入れたいだけなら、pipx も選択肢です。pipx はツールごとに隔離した環境を自動で作るので、pipx install <ツール名> で system を汚さずに入れられます。

--break-system-packages の何が危ないか

エラーメッセージには、--break-system-packages を付ければ上書きできる、とも書かれています。

# 通ってしまうが、名前のとおり「system を壊しうる」
pip install --break-system-packages requests

これはたしかにインストールを通しますが、マーカーが止めていた衝突そのものは避けられていませんpip が system の場所に入れたパッケージが、apt が管理する同名パッケージと食い違うと、OS のツール(command-not-foundapt-listchangesunattended-upgrades のような Debian の Python 製ツール)が動かなくなることがあります。名前が示すとおり、これは「壊れてもよいと分かっているとき」の逃げ道です。

一時的なコンテナで、後から作り直せる環境なら、この上書きが実害にならない場合もあります。しかし、あなたが日常使う OS の system Python に対しては避けてください。ほとんどの場面では、venv か pipx で入れる場所を分けるほうが、後で困りません。

切り分け(うまくいかないとき)

  • python3 -m venv 自体が失敗する:Debian / Ubuntu では venv が標準で入っていないことがあります。sudo apt install python3-venv(バージョン付きの python3.11-venv などが必要な場合もあります)を入れてから作り直してください。
  • --break-system-packages を付けても別のエラーになる:それはマーカーとは別の原因(ビルドの失敗、依存の衝突など)です。マーカーの拒否は externally-managed-environment という文言で出るので、別の文言なら別の記事の対象です。
  • pip install --user なら通ると聞いた--user もマーカーのある環境では同じく拒否されます(PEP 668 は user site への install も対象に含みます)。確実なのは venv か pipx です。
  • システムのツールとして本当に入れたいapt にそのパッケージがあるなら、sudo apt install python3-<パッケージ名> で OS の管理下に入れるのが筋です。マーカーは元々「OS の管理に寄せてほしい」という宣言なので、その方向にそろえるのが衝突を生みません。

検証環境

  • debian:12(マーカー有り・Python 3.11.2)で再現、同環境の venv で修正。対照として python:3.12-slim(マーカー無し)では出ないことも確認
  • 再現:pip3 install requestserror: externally-managed-environment で終了コード 1
  • 修正:python3-venv を入れて python3 -m venv で仮想環境を作り、その中で pip install requests → 終了コード 0

再現から修正までは errfix の検証ハーネスが機械的に確認しています。EXTERNALLY-MANAGED マーカーを持つ環境で system への pip install が拒否されること、仮想環境の中では同じ install が通ることを、再現→修正→シグネチャ消滅として通しました。マーカーの仕組みは PEP 668、各ディストリビューションの採用状況はそれぞれの公式情報に拠ります。

検証(machine-verified)

この修正は debian:12 のバージョン固定コンテナ内で再現し、修正後に externally-managed-environment のシグネチャが消えることを機械で確認しています。

verify — run-case.mjs
$ node run-case.mjs python/pip-externally-managed-environment
● reproduce externally-managed-environment present ✓
● apply fix exit 0
● re-run externally-managed-environment gone ✓
PASS verified · debian:12 · signature gone