Error [ResolutionImpossible]

ResolutionImpossible の直し方(pip install が依存の衝突で止まる)

FIX SUMMARY verified
Applies when
python:3.12-slimpip (new resolver, default since 20.3)conflicting version pins (requests==2.25.0 requires urllib3<1.27, but urllib3==2.0.0 pinned)

Verified: reproduced in python:3.12-slim, then the ResolutionImpossible signature was gone after the fix (exit 0).

pip install が、パッケージを1つも入れないまま次のエラーで止まることがあります。

ERROR: Cannot install requests==2.25.0 and urllib3==2.0.0 because these package versions have conflicting dependencies.
ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/topics/dependency-resolution/#dealing-with-dependency-conflicts

これは「あるパッケージが要求する依存のバージョンと、いま入れようとしている構成が食い違っている」という報告です。上の例なら、requests==2.25.0 が求める urllib3 の範囲に、同時に指定した urllib3==2.0.0 が入っていません。

このエラーで検索すると、--use-deprecated=legacy-resolver を付けろ、という回避策がよく出てきます。これはたしかにエラーを消しますが、衝突そのものは残ったままのことがあります。その理由は、pip のバージョンによる依存解決の違いにあります。

まず、何が衝突しているのかを読む

ResolutionImpossible のメッセージは、読む場所が決まっています。上の1行目がすべてです。

Cannot install requests==2.25.0 and urllib3==2.0.0 because these package versions have conflicting dependencies.

requests==2.25.0urllib3==2.0.0 が両立しない、と名指しされています。なぜ両立しないかは、requests==2.25.0 側の要求を見ると分かります。requests の 2.25.0 は、urllib3 を次の範囲で要求しています(パッケージのメタデータに書かれています)。

Requires-Dist: urllib3 (<1.27,>=1.21.1)

urllib3==2.0.0 はこの <1.27 の外にあります。だから「requests==2.25.0 を入れるなら urllib31.27 未満でなければならないのに、2.0.0 を指定した」という食い違いになり、pip は両立する組み合わせを見つけられずに停止します。要求している側(requests)と、要求されている側(urllib3)のどちらの版を動かせば範囲に収まるかが、そのまま直し方になります。

なぜ「前は入っていた」のか:pip の新しい依存解決

このエラーは、あなたの指定が変わったから急に出たとは限りません。pip 自身の依存解決の挙動が変わったために、以前は黙って通っていた組み合わせが止まるようになった、という場合があります。

pip は 20.3(2020年)で新しい依存解決器(resolver)を既定にしました。それより前の pip は、依存の衝突をきちんと検査せず、要求を満たさない組み合わせでもそのまま入れていました。新しい resolver は、全体で矛盾のない組み合わせを探し、見つからなければ ResolutionImpossible で止まります。

その結果、「手元(まだ古い pip)では入るのに、新しい pip を積んだ CI やチームメンバーでだけ止まる」 というすれ違いが起こります。止まった環境の pip --version を先に確認してください。20.3 以降なら、新しい resolver が衝突を検出しています。

直し方:衝突する版を揃える

正しい対処は、メッセージが名指しした2つの版を、要求を満たす形にそろえることです。方向は2つあります。

要求されている側(urllib3)を、要求元が許す範囲に下げる。 requests==2.25.0urllib3<1.27 を求めているので、urllib3 をその範囲に収めます。

pip install "requests==2.25.0" "urllib3==1.26.20"

要求元(requests)を、新しい urllib3 に対応した版に上げる。 requests は 2.30.0 で urllib3 2.x への対応を入れました。新しい urllib3 を使いたいなら、requests をその版以上に上げます。

pip install "requests>=2.30" "urllib3==2.0.0"

どちらに寄せるかは、urllib3 2.x の機能が必要か、requests を上げてほかの依存に影響が出ないか、で決めます。いずれの場合も、ResolutionImpossible を黙らせたのではなく、衝突の原因そのものを消しています。実際に requirements ファイルを使っているなら、片方だけを固定してもう片方は範囲指定にすると、pip が両立する版を選べます。

--use-deprecated=legacy-resolver で入れても、直ってはいない

このエラーでよく勧められる --use-deprecated=legacy-resolver は、pip 20.3 より前の古い依存解決の挙動に戻す指定です。これを付けると、先ほどの衝突した組み合わせも入ってしまいます。

Successfully installed ... requests-2.25.0 urllib3-2.0.0

ただし、これは衝突を解決したのではなく、検査を外しただけです。入った先が本当に両立するとは限りません。この requests==2.25.0urllib3==2.0.0 の組み合わせは、requests 自身が起動時に警告します。

RequestsDependencyWarning: urllib3 (2.0.0) or chardet (3.0.4) doesn't match a supported version!

requests==2.25.0 は、テスト済みの urllib3<1.27 という上限を切っています(この版が出た当時、urllib3 は 1.x 系でした)。urllib3 はその後、この上限の外側で 2.0 を出しました。古い resolver はこの上限の宣言を無視して入れるので、requests==2.25.0 が想定していない urllib3 を掴んだ状態になります。この状態は、importSession() の作成までは通ってしまうことがあり、実際に不整合が表面化するのは、その urllib3 の変わった部分にコードが触れたときです。エラーが install 時点で出ないぶん、気づくのが遅れやすい形です。

古い pip や legacy-resolver は、原因の分からないまま最初に打つ手ではなく、「この組み合わせでも本当に動く」と自分で確かめられるときに限った逃げ道です。ライブラリ側の要求が実態より厳しすぎて、実際には動く組み合わせもあります。その見極めができないなら、衝突そのものを解く(版をそろえる)ほうが安全です。なお --use-deprecated=legacy-resolver は名前のとおり pip 自身が非推奨としている指定で、将来の pip で使えなくなりえます。恒久策には向きません。

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

  • 要求元が第三者ライブラリで、自分では版を動かせないurllib3 のように直接指定した依存だけでなく、あるライブラリを入れようとしたら、そのライブラリが要求する依存が既存の構成とぶつかる形でも、同じ ResolutionImpossible になります。まず、いまの構成に対応した要求元の新しい版が無いかを確認します。無ければ、要求元に合わせて自分の依存を戻せるか、別のライブラリに替えられるか、上流の更新を待つかを検討します。
  • メッセージが長く、どの版がぶつかっているか読み取れない:pip は候補をさかのぼって探すため、途中経過が大量に出ることがあります。読むのは最後の Cannot install X and Y because ... の行です。そこで名指しされた2つが、そろえる対象です。
  • pip install は通ったのに、実行時に ImportErrorAttributeError が出る:これは resolver が衝突を止められなかった側の問題です。依存の版指定が緩く、両立しない組み合わせが入ってしまうと、install は成功しても実行時に壊れます。pip check で、入っている構成に矛盾が無いかを確認できます(ただし上限の無い要求が原因だと pip check でも検出されないことがあります)。典型は Flask と Werkzeug で、Flask が新しい Werkzeug で ImportError になる例で扱っています。
  • --use-deprecated=legacy-resolver を付けたら通った:それは衝突が解決したのではなく、検査を外しただけです。pip check や、実際に使う機能を通すテストで、その組み合わせが本当に動くかを確かめてください。暫定利用なら、解除する条件を残しておきます。

検証環境

  • python:3.12-slim、pip の新しい依存解決器(既定)、ネットワーク有り
  • 再現:pip install "requests==2.25.0" "urllib3==2.0.0"ResolutionImpossible で終了コード 1
  • 修正:pip install "requests==2.25.0" "urllib3==1.26.20"(衝突しない組み合わせ)→ 終了コード 0

再現から修正までは errfix の検証ハーネスが機械的に確認しました。衝突する版の組み合わせが ResolutionImpossible で止まること、両立する版にそろえると通ることを、再現→修正→シグネチャ消滅として通しています。requests==2.25.0urllib3 (<1.27,>=1.21.1) を要求することはパッケージのメタデータで、新しい依存解決器が pip 20.3 で既定になったことは pip の公式情報で確認しました。--use-deprecated=legacy-resolver が同じ組み合わせを入れること、その構成で requestsRequestsDependencyWarning を出すことも実測しています。

検証(machine-verified)

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

verify — run-case.mjs
$ node run-case.mjs python/pip-resolution-impossible
● reproduce ResolutionImpossible present ✓
● apply fix exit 0
● re-run ResolutionImpossible gone ✓
PASS verified · python:3.12-slim · signature gone