import requests や import urllib3(あるいはそれらに依存するライブラリの読み込み)で、次のエラーが出て止まることがあります。
ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'OpenSSL 1.1.0l 10 Sep 2019'. See: https://github.com/urllib3/urllib3/issues/2168
'OpenSSL ...' の部分は環境ごとに変わり、OpenSSL 1.0.2k のように別の版番号になることもあります。先頭の v2.0 も、urllib3 2.0.x では v2.0、2.1 以降では v2 と表示が変わる可変部分です。これは、入っている urllib3 が 2 系で、その Python がリンクしている OpenSSL が 1.1.1 より古いために起きます。
なぜ import で止まるのか
urllib3 は 2.0(2023 年)で、OpenSSL 1.1.1 未満のサポートを打ち切りました。1.1.1 で入った API に依存する実装へ切り替えたためです。urllib3 2 系は読み込まれた瞬間(urllib3/__init__.py)に、Python の ssl モジュールがどの OpenSSL でコンパイルされたかを調べ、1.1.1 より古ければその場で ImportError を投げます。requests は内部で urllib3 を使うので、import requests でも同じ経路をたどります。
止まっているのはインストールではなくimport です。pip install は成功していて、実行時に初めて版の不一致が表面化します。
なぜ古い OpenSSL の環境で出るのか
手元の開発機は OpenSSL が新しい(1.1.1 以降、あるいは 3.x)ので、urllib3 2 系がそのまま読み込めます。エラーになるのは、Python がリンクしている OpenSSL が 1.1.1 より古い環境です。次のような場所で出ます。
- Amazon Linux 2(OpenSSL 1.0.2):EC2 の AL2 で踏みやすい定番です。AWS Lambda では Python 3.8〜3.11 ランタイム(Amazon Linux 2・OpenSSL 1.0.2)で踏み、3.12 以降(Amazon Linux 2023・OpenSSL 3.x)では踏みません。
- 古い Debian / Ubuntu:Debian 9(stretch)は 1.1.0、Ubuntu 16.04 は 1.0.2 で、いずれも境界を下回ります。
- CentOS / RHEL 7(OpenSSL 1.0.2)。
同じ Python の版でも、リンクされている OpenSSL の版で出たり出なかったりします。手元(新しい OpenSSL)では通るのに、古い OS のコンテナや CI、Lambda で落ちる、という形になります。
直し方1:urllib3 を 1 系に固定する(OpenSSL を上げられないとき)
OpenSSL を上げられない環境(レガシー OS、AL2 の Lambda など)では、urllib3 を 2 未満に固定するのが第一手です。1.26 系は古い OpenSSL でも動きます。
pip install "urllib3<2"
requirements.txt や制約ファイルに書くなら次のようにします。
urllib3<2
requests を使っていて requests 自身は据え置きたい場合も、依存する urllib3 側を固定すれば読み込めるようになります。すでに 2 系が入っているなら、pip install "urllib3<2" で入れ直せば 1.26 系へ下がります。
これは版を固定するだけなので、OpenSSL には触れません。urllib3 1.26 系は新機能の追加は終了していますが、セキュリティ修正は継続しています。古い環境をそのまま使うあいだの措置と考え、OpenSSL 1.1.1+ に移行できたらこのピンは外します。
直し方2:OpenSSL 1.1.1+ の環境へ上げる(恒久)
urllib3 2 系を使い続けるなら、OpenSSL が 1.1.1 以降の環境に移すのが本筋です。urllib3 の版でなく土台を直すので、他のライブラリの将来の要求にも耐えます。
- コンテナ / OS を新しくする:Debian なら bullseye・bookworm(OpenSSL 1.1.1/3.x)、Amazon Linux なら AL2023 に上げます。ベースイメージのタグを新しいものへ替えるだけで OpenSSL も上がります。
- macOS:Xcode Command Line Tools 付属の Python(
/usr/bin/python3)は LibreSSL にリンクされていますが、urllib3 2 系の import 自体は成功します(この ImportError にはならず、別問題です)。どうしても OpenSSL 版が必要なら、python.org 版や Homebrew の Python を使います。これらは新しい OpenSSL にリンクされています。 - どの OpenSSL でビルドされているかを確認する:次で今の版が分かります。
python -c "import ssl; print(ssl.OPENSSL_VERSION)"
これが 1.1.1 以降(または OpenSSL 3.x)になっていれば、urllib3 2 系はそのまま読み込めます。1.1.0/1.0.2 など実 OpenSSL の 1.1.1 未満と出るなら、直し方1で固定するか、環境を上げます。LibreSSL … と出る場合はこの ImportError ではなく別問題なので、後述の切り分けを参照してください。
切り分け(うまくいかないとき)
import requestsで出た:requests が依存する urllib3 が原因です。requests を下げる前に、python -c "import ssl; print(ssl.OPENSSL_VERSION)"で OpenSSL の版を確かめ、urllib3 側を固定するか環境を上げます。urllib3<2を入れたのに、まだ 2 系が読み込まれる:別の依存が urllib3 2 系を引き戻していないかをpip show urllib3で確認します。制約ファイル(pip install -c constraints.txt -r requirements.txt、中身にurllib3<2)で上限を揃えられます。ただし別の依存がurllib3>=2を要求している場合は両立せず解決エラーになるので、その依存を対応版へ更新するか OpenSSL を上げます。ssl.OPENSSL_VERSIONがLibreSSL …の場合:urllib3 2 系は import 時に別の警告(NotOpenSSLWarning)を出しますが、import 自体は成功し、この ImportError にはなりません。macOS の/usr/bin/python3(Xcode Command Line Tools・LibreSSL)が典型で、TLS 機能に不安があれば python.org や Homebrew の OpenSSL 版 Python を使います。これは本記事の ImportError とは別問題です。- エラーが出ずに
SSL: CERTIFICATE_VERIFY_FAILEDなどの別の SSL エラーになる:それは版の不一致ではなく証明書側の問題で、原因も対処も別です。まず今回のimportが通るかを先に確認してから切り分けます。
検証環境
python:3.7-slim-stretch(OpenSSL 1.1.0l)、ネットワーク有り- 再現:
pip install "urllib3>=2"の後にimport urllib3すると、urllib3 v2.0 only supports OpenSSL 1.1.1+を出して終了コード 1 - 修正:
pip install "urllib3<2"の後にimport urllib3すると、終了コード 0
再現から修正までは errfix の検証ハーネスが機械的に確認しています。境界が OpenSSL の版であって Python の版でないことは、対照として python:3.7-slim-buster(OpenSSL 1.1.1n・同じ Python 3.7)で urllib3 2 系が読み込めることを確かめて裏づけました。エラー文言は実機出力から引用しています。Amazon Linux 2(OpenSSL 1.0.2)や古い Debian/Ubuntu・CentOS/RHEL 7 といった実 OpenSSL 1.1.1 未満の環境については、機械検証の対象は上記の 2 点に限り、一般的な引き金として urllib3 の issue #2168 に沿って書いています。なお macOS の LibreSSL はこの ImportError の引き金ではなく(NotOpenSSLWarning になり import は成功します)、別問題です。