Claude Media
AnthropicのPython SDKでプロキシ環境変数が反映されない問題を解決する

AnthropicのPython SDKでプロキシ環境変数が反映されない問題を解決する

企業プロキシ環境でAnthropicのPython SDKがAPIConnectionErrorを返す既知の不具合と、現行バージョンの自動検出の仕組み・明示設定の方法を解説します。

AnthropicのPython SDKで起きるプロキシ関連のAPIConnectionError

企業ネットワーク配下でAnthropicのPython SDKを使うと、http_proxyやhttps_proxyを設定していてもanthropic.APIConnectionError: Connection error.が発生することがあります。SDKがプロキシ環境変数を読み込まず、社内ネットワークを経由せず直接インターネットへ接続しようとして失敗するためです。

APIConnectionErrorはネットワーク到達不能を示すSDK共通の例外クラスです。DNS解決の失敗やTLSハンドシェイクの失敗もこのクラスで表現されますが、プロキシ未適用もその一因になります。実際にGitHub issue #923では、anthropic==0.49.0・httpx==0.27.0の組み合わせでhttp_proxy/https_proxyを設定してもSDKの接続に反映されないと報告されています。

v0.47.0〜v0.53.xでプロキシ環境変数が無視されていた理由

原因はSDK側が持つカスタムトランスポートでした。SDKはidleタイムアウトを避けるためTCPのkeep-alive挙動を細かく制御しており、そのために内部でhttpx.HTTPTransportを独自に組み立てています。

このカスタムトランスポートを渡すと、httpx自身が持つ「環境変数からプロキシを自動検出する」機能が無効になります。関連issue#878は、v0.46.0では問題なく動いていたのにv0.47.0へ上げたらhttps_proxyが効かなくなったと報告しています。トランスポートの組み立て方が変わった時期と一致する症状です。

PyPIの公開日で数えると、v0.47.0(2025年2月24日)から修正版までの約3か月半、httpx単体なら動くはずの環境変数が、SDKの内部実装との競合で無視される状態が続いたことになります。型にまだ無い項目の送受信はAnthropic Python SDKのextra_bodyとmodel_extraにまとめています。

v0.54.0の修正内容 — 独自のプロキシ検出ロジックを追加

Anthropicはこの競合をPR #969で修正し、v0.54.0(2025年6月10日リリース、PyPI公開は6月11日)に取り込みました。修正の核は新設された_utils/_httpx.pyのget_environment_proxies()関数です。

この関数は標準ライブラリのurllib.request.getproxies()でプロキシ設定を取得し、http・https・allの各スキームに対応するマウント先と、no_proxyで除外するホストのリストを作ります。SDKはこの結果をもとにhttpx.HTTPTransportを個別のマウントとして組み立て直し、環境変数のプロキシ設定を独自トランスポートと共存させています。

issue #923にはSDK側の開発者から「v0.54.0を試してほしい。httpのプロキシ環境変数サポートを意図せず外していたのを戻した」というコメントが付きました。ユーザー側のコードを変更しなくても、SDKをアップグレードするだけで直る不具合だったことになります。issue報告者自身が回避策としてhttpx.Clientを自作しhttp_client引数に渡す方法を提示していましたが、v0.54.0以降はその回避策なしでも動きます。

v1.xでもこの自動検出は生きている

SDKはv1.0.0(2026年8月20日リリース)でHTTPバックエンドをhttpxから、API互換のフォークであるhttpx2へ移行しました。破壊的変更を伴う移行でしたが、v0.54.0で追加されたプロキシ自動検出は、移行後の_base_client.pyでもget_environment_proxies()を呼ぶ形で残っています。

あゆみ

プロキシ環境変数の扱いの移り変わり

  1. 〜v0.46.0環境変数のプロキシが効く

    SDKがカスタムトランスポートを組み立てる前の挙動です。

  2. v0.47.0〜v0.53.x独自トランスポートとの競合で無視される

    後述の実機確認では、v0.53.0でHTTPS_PROXYを設定してもマウントが空のままでした。

  3. v0.54.0(2025-06-10)get_environment_proxies()を追加して修正

    PR #969で修正。

  4. v1.0.0(2026-08-20)HTTPバックエンドがhttpx2へ移行

    移行後も維持。

  5. v1.11.0同じ仕組みが動作

    HTTPS_PROXYを設定するだけでhttps://のマウントが入ることを確かめています。

AnthropicのAPIエンドポイントは常にTLSでの接続になるため、実務上重要になるのはhttps_proxy(またはHTTPS_PROXY)です。http_proxyだけを設定していても、API呼び出し自体には影響しません。この不具合はトランスポート層の問題なので、同じクライアントを使う呼び出しは、ストリーミングやMessage Batches、tool_runnerも含めて影響を受けます。エンドポイントごとに個別対応する必要はありません。

http_clientに何を渡すと環境変数が効かなくなるか

http_clientを渡すと環境変数が効かなくなるかどうかは、渡す中身で変わります。httpx2を入れたanthropic==1.11.0と、httpx==0.27.2を入れたanthropic==0.53.0でHTTPS_PROXY=http://proxy.example:8080を設定し、クライアントの_mounts(URLのパターンと使う接続先の対応)を比べました。

渡し方v0.53.0v1.11.0
Anthropic()v0.53.0{}(無視される)v1.11.0https://にプロキシ
DefaultHttpxClient()v0.53.0{}(無視される)v1.11.0https://にプロキシ
素のhttpx.Client() / httpx2.Client()v0.53.0https://にプロキシv1.11.0https://にプロキシ
DefaultHttpxClient(transport=...)v0.53.0{}(無視される)v1.11.0{}(無視される)
DefaultHttpxClient(proxy="...")v0.53.0all://のみv1.11.0https://(環境変数)とall://(明示)

つまり落とし穴はhttp_clientを渡すこと自体ではなく、transport=を渡すことです。Anthropic()と引数なしのDefaultHttpxClient()ではSDKのget_environment_proxies()が、素のhttpx2.Client()ではhttpx2自身の環境変数対応が働きます。transport=を渡すとどちらの検出も走らないため、proxy=の明示が要ります。もともとのバグも、SDKがカスタムトランスポートを内部で渡していたことから起きました。

もう1点、v1.11.0ではproxy=を明示しても、環境変数のHTTPS_PROXYが残っているとHTTPSの宛先は環境変数側のプロキシに向かいました。HTTPS_PROXY=http://envproxy.example:8080のもとでproxy="http://explicit.example:3128"を指定し、https://api.anthropic.comに使われる接続先を調べるとenvproxy.exampleだったのに対し、http://の宛先はexplicit.exampleでした。明示した値を使わせたいときは、環境変数側を外しておきます。

trust_env=Falseを渡したDefaultHttpxClientでも、v1.11.0では環境変数のHTTPS_PROXYがhttps://のマウントとして入りました。環境変数のプロキシを使わせたくないときは、trust_env=FalseではなくNO_PROXYを使います。

verify・certの設定はプロキシ経由の接続にも引き継がれる

_base_client.pyの実装を見ると、プロキシ用のマウントを作る際にverify・cert・trust_env・http1・http2・limitsの各引数を、通常のトランスポートと同じ値で渡しています。

社内CAの証明書でTLSを中継するプロキシ環境では、DefaultHttpxClient(proxy=..., verify="/path/to/ca-bundle.pem")のようにverifyを指定すれば、その証明書検証はプロキシ経由の接続にも適用されます。プロキシ設定だけ通っても証明書エラーで止まる場合は、このverify引数を疑うのが近道です。

ANTHROPIC_BASE_URLという環境変数も存在しますが、これはAPIの接続先URLそのものを差し替えるためのもので、プロキシとは別の仕組みです。自前のゲートウェイを経由させたい場合はこちらを使い、企業プロキシ経由でapi.anthropic.comへ到達したいだけの場合はプロキシ環境変数の話に限定して考えます。

NO_PROXYで特定ホストだけプロキシを迂回する

get_environment_proxies()はno_proxy環境変数もそのまま解釈します。値が*ならプロキシ検出自体を無効にし、個別のホスト名を指定すればそのホストだけ直接接続に切り替わります。

ホスト名がIPv4・IPv6アドレスやlocalhostの場合は完全一致でマウントがNoneになり、それ以外の文字列は末尾一致のパターンで除外されます。HTTPS_PROXYとNO_PROXY=internal.example.com,localhost,10.0.0.5を設定してv1.11.0のAnthropic()を作ると、_mountsは次のようになりました。

{'all://*internal.example.com': None, 'all://localhost': None,
 'all://10.0.0.5': None, 'https://': 'HTTPTransport'}

Noneのエントリが、そのホストをプロキシに通さない指定です。NO_PROXY=*を設定すると、Anthropic()でもDefaultHttpxClient()でも_mountsは空になり、HTTPS_PROXYがあっても使われません。社内APIサーバーなど、Anthropic以外の宛先だけプロキシを迂回させたい場合は、NO_PROXYにそのホスト名をカンマ区切りで列挙します。

transport付きのhttp_clientではプロキシも明示する

プロキシが必要な環境でtransport=付きのhttp_clientを使うなら、DefaultHttpxClientにproxyを明示します。

import httpx2
from anthropic import Anthropic, DefaultHttpxClient
 
client = Anthropic(
    http_client=DefaultHttpxClient(
        proxy="http://my.proxy.example.com:8080",
        transport=httpx2.HTTPTransport(local_address="0.0.0.0"),
    ),
)

この組み合わせにHTTPS_PROXYを併せて設定してv1.11.0で確かめると、_mountsはall://のマウント1つだけで、環境変数ではなくproxy=の値が使われます。NO_PROXYの除外も働かなくなるため、迂回したいホストがあるならmountsで自分で組み立てます。

生のhttpx2.ClientではなくDefaultHttpxClientを使うのは、SDKが前提とするデフォルトのタイムアウトや接続数上限を保つためです。素のhttpx2.Clientを渡すとこれらの既定値が失われます。

非同期クライアントではAsyncAnthropicとDefaultAsyncHttpxClientの組み合わせが同じパターンに対応します。

import httpx2
from anthropic import AsyncAnthropic, DefaultAsyncHttpxClient
 
client = AsyncAnthropic(
    http_client=DefaultAsyncHttpxClient(
        proxy="http://my.proxy.example.com:8080",
    ),
)

同期・非同期どちらを選ぶかは並列度の要件で決まる話で、プロキシ設定の考え方自体は共通です。実装の違いやaiohttpバックエンドとの使い分けはClaudeのPython SDKで非同期実行とtool_runnerを実装するで扱っています。

動作確認の手順

プロキシが効いているかどうかは、SDKの内部に入ったマウントを見るのが確実です。

手順

切り分けの手順

  1. 1

    SDKのバージョンを見る

    0.54.0未満なら、アップグレードだけで直る可能性が高い状況です。

  2. 2

    環境変数を設定して_mountsを見る

    HTTPS_PROXYを設定した状態で、次の確認コードを実行します。

  3. 3

    出力を読む

    https://のキーが出ていれば、環境変数のプロキシがSDK内部の接続に反映されています。

python -c "import anthropic; print(anthropic.__version__)"
pip install --upgrade anthropic
export HTTPS_PROXY="http://my.proxy.example.com:8080"
python -c "
from anthropic import Anthropic
client = Anthropic()
print({k.pattern: type(v).__name__ for k, v in client._client._mounts.items()})
"

v1.11.0では{'https://': 'HTTPTransport'}と出力されました。v0.53.0では同じコードが{}を返します。URLにuser:password@を埋め込んだ認証付きプロキシも、環境変数側で同じ書式のままhttps://のマウントとして認識されました。

Bedrock向けのAnthropicBedrockも同じSyncAPIClientを継承しており、v1.11.0でHTTPS_PROXYを設定するとhttps://のマウントが入りました。

古いDockerイメージやバージョンのピン留めがこの不具合を今も再発させる

v0.54.0のリリースからすでに1年以上が経っています。それでもrequirements.txtでanthropic==0.49.0のようにバージョンをピン留めしたままのプロジェクトや、ビルドキャッシュを使い回すDockerイメージでは、この不具合が今も再現します。

依存関係のロックファイルを使うプロジェクトでは、anthropic本体を直接ピン留めしていなくても、他のパッケージが古いanthropicを間接的に要求してロックされたままになっているケースもあります。ロックファイルの更新頻度が低いリポジトリほど、この不具合に当たる確率が上がります。

APIConnectionErrorのメッセージ自体はプロキシに触れないため、タイムアウトやDNSの問題と見分けがつきにくく、原因の切り分けに時間がかかりやすい不具合です。他のAPIErrorサブクラス(RateLimitErrorやAPIStatusErrorなど)との違いはClaude APIのエラー形式とSDK例外クラスの言語別対応表にまとめています。

v1.0.0のhttpx2移行はSDKの破壊的変更を伴うため、0.x系から上げる際は移行ガイドの確認が前提になります。移行の実務手順は/claude-api upgradeでPython SDKをv0.xからv1へ移行するで扱っています。

まとめ

http_proxy/https_proxyが効かないときは、まずSDKのバージョンが0.54.0以上かを確かめ、次にtransport=付きのhttp_clientを渡していないかを見ます。どちらにも当てはまらないのに_mountsが空なら、NO_PROXY=*が残っていないかを疑います。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. github.com/anthropics/anthropic-sdk-python/issues/923
  2. github.com/anthropics/anthropic-sdk-python/pull/969
  3. github.com/anthropics/anthropic-sdk-python/issues/878
  4. github.com/anthropics/anthropic-sdk-python/releases/tag/v0.54.0
  5. pypi.org/project/anthropic
  6. platform.claude.com/docs/en/cli-sdks-libraries/sdks/python
  7. docs.python.org/3/library/urllib.request.html
この記事を共有:XはてブLinkedIn