Cowork のスケジュールタスクで Python の小さなツールを呼びたくなったのは、ちょうど4サイトの記事自動生成パイプラインに品質ゲートを差し込み始めた頃でした。pandas で軽い集計をしたいだけ。たった一行のはずでした。
$ pip install pandas
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.
Ubuntu 22.04 ベースのサンドボックスで pip を実行すると、こんなエラーで止まります。sudo の話でも、ネットワークの話でも、PyPI の停止でもありません。Python 3.11 以降で広く採用された PEP 668(Externally Managed Environments)という設計判断が、システム全体の Python を壊さないように pip を意図的に拒否しているのです。
私は廣川政樹というアプリ開発者兼アーティストで、2014 年から個人で iOS/Android アプリを 5,000 万 DL 超届けてきました。Cowork のスケジュールタスクで毎日4サイトの記事自動投稿を回しながら、Python の小さなユーティリティを差し込みたい場面が頻繁に出てきます。同じエラーで止まった方の参考になるよう、診断手順と3つの解決パターンを整理します。
なぜ PEP 668 は pip を拒否するのか
Ubuntu や Debian のような Linux ディストリビューションでは、システムの一部の機能が Python で書かれています。apt 自体も内部で Python を使っているコンポーネントがありますし、Cowork のような VM サンドボックスでは Node や ripgrep などのツールチェインがシステム Python に依存しているケースがあります。
ここに pip install でグローバルパッケージを上書きすると、システムが期待しているライブラリのバージョンが知らないうちに変わってしまい、別のツールが動かなくなるリスクがあります。PEP 668 はその事故を未然に防ぐため、配布元(OS ベンダー)が EXTERNALLY-MANAGED というマーカーファイルを置いておくと、pip が「これは触らないでね」と空気を読んで拒否する仕組みです。
確認はシンプルです。
ls /usr/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null && echo "PEP 668 環境です"Cowork のサンドボックスで実行すると、/usr/lib/python3.10/EXTERNALLY-MANAGED が見つかり、PEP 668 の対象であることが分かります。エラーは仕様通りの動作であって、バグではありません。
解決パターン 1: --break-system-packages で意図を明示する
最小工数で動かしたいときの定番がこちらです。pip に「壊れることを承知の上で入れる」と明示すれば通ります。
pip install pandas --break-system-packagesスケジュールタスクのワンライナーや、軽い集計のために 1〜2 パッケージだけ入れたい場面では十分です。Cowork のシステム Python は VM ごとに使い捨てに近い扱いなので、システムを壊しても次のセッションで作り直されます。実害が小さい環境では合理的な選択肢です。
注意したい点は2つあります。1つ目は、pip install が成功しても、システム同梱の同名パッケージとバージョンが衝突するケースです。pip show で実体の場所を必ず確認します。
pip show pandas | grep Location
# Location: /usr/local/lib/python3.10/dist-packages/usr/local/lib/.../dist-packages に入っていれば、システム同梱の /usr/lib/python3/dist-packages を上書きしていません。Python のインポート優先順位的にも /usr/local 側が先になるので、新しい方が読まれます。
2つ目は、CI のような清潔な環境ではこのフラグを使わない方が良い、という点です。後述する venv の方が再現性が高く、依存関係も追跡しやすくなります。
解決パターン 2: venv で隔離する(推奨)
少しだけ手間が増えますが、毎回のスケジュールタスクで使うなら venv が一番素直です。
python3 -m venv /tmp/venv
source /tmp/venv/bin/activate
pip install pandas requests
python my_script.pyvenv の中では PEP 668 のマーカーが効かないので、--break-system-packages も sudo も要りません。スクリプトを抜けたら /tmp/venv をそのまま削除すれば後始末も済みます。
Cowork のスケジュールタスクで使うときは、bash の単発呼び出しに環境変数が引き継がれないことに注意します。1回の bash 呼び出しの中で source から実行まで完結させるか、/tmp/venv/bin/python をフルパスで呼び出すパターンが安全です。
# 1回の bash 呼び出しに収める
python3 -m venv /tmp/venv && \
/tmp/venv/bin/pip install -q pandas requests && \
/tmp/venv/bin/python /sessions/*/mnt/work/script.pyスケジュールタスクの SKILL.md からこの形で呼べば、サンドボックスの再起動を挟んでも毎回クリーンな環境で動きます。私が記事自動投稿のパイプラインを安定させる過程で、最終的に落ち着いたのもこの形でした。
解決パターン 3: pipx で CLI ツールを孤立インストール
black や ruff のような単体 CLI を入れたい場合は pipx が便利です。pipx は内部で自動的に venv を作り、PATH の通った場所に CLI シンボリックリンクだけを置いてくれます。
pip install pipx --break-system-packages
pipx install ruff
ruff --version注意点として、pipx 自身のインストールには結局 --break-system-packages が必要です。これは pipx をシステムワイドにブートストラップするための一度きりの妥協で、その後の pipx install は全てクリーンな venv の中に閉じ込められます。
ライブラリではなく CLI を 5〜10 個入れたい運用なら、pip install --break-system-packages を連発するよりも pipx の方が結果的に環境が綺麗に保たれます。
どのパターンを選ぶか
- ワンショットの集計・実験 →
--break-system-packages - スケジュールタスクで定期実行 →
python3 -m venv /tmp/venv - CLI ツールを複数入れたい →
pipx install - パッケージ間の依存解決を確実にしたい → venv(または
uv)
最近私が新規プロジェクトで好んで使うのは uv です。pip 互換のインターフェースを保ちつつ、venv の作成からインストールまで桁違いに速く動きます。Cowork のサンドボックスのように毎回環境を作り直すケースでは、起動の速さがそのまま実行時間の短縮になります。
pip install uv --break-system-packages
uv venv /tmp/venv
uv pip install -p /tmp/venv/bin/python pandasただし uv 自体を --break-system-packages で入れる必要があるのは pipx と同じ事情です。
ありがちな落とし穴
--user フラグで回避しようとしてもエラー: 一見すると pip install --user pandas で $HOME/.local に逃がせそうですが、PEP 668 はユーザーインストールも拒否します。--break-system-packages を併用しない限り通りません。
sudo pip install は最悪手: 「root なら通るのでは」と試したくなりますが、PEP 668 はそもそも root に対する保護を意図しています。sudo --break-system-packages で通っても、システムの apt 管理下のパッケージを上書きする可能性が最も高いのがこの組み合わせです。Cowork のような使い捨て VM 以外では絶対に避けます。
venv を作ったのにパッケージが見えない: source を別の bash 呼び出しでやっている可能性があります。Cowork の bash は各呼び出しが独立しているため、source の効果は次の呼び出しに残りません。1回の呼び出しに収めるか、フルパスで python3 を呼びます。
requirements.txt の前に pip install --upgrade pip がエラー: pip 自身を更新しようとしても PEP 668 で弾かれます。venv の中であれば自由に上げられるので、まず venv に入ってから pip install --upgrade pip を実行します。
次にやってほしいこと
エラーに遭遇したら、まず ls /usr/lib/python3.*/EXTERNALLY-MANAGED で本当に PEP 668 環境かを確認してください。エラーメッセージは似ていても、原因が違うことがあります。確認できたら、上記の表を頼りに今回のユースケースに合うパターンを1つだけ選びます。最初から「全部 venv」にすると、ワンショットの実験で過剰装備になりがちです。
私自身、最初は --break-system-packages を多用していましたが、スケジュールタスクが増えるにつれて venv の方が運用が楽になりました。実装の参考になれば幸いです。