10月14日、あるOSSのGitHubに「Claudeのレビュー機能がtemperatureで400エラーになる」という報告が上がりました。困ったのは、エラー処理がこの400を「APIの障害」と判断し、レビューが何も言わずに止まっていたことです。修正はtemperatureを送らないようにするだけでした。
この症状は、Claude APIの新バージョン「2026-10-21」で全モデルに広がります。temperature・top_p・top_kは、既定値(temperatureなら1.0)を送っただけでも400で拒否されます。しかもPythonでは、依存の書き方によっては次のインストールでSDKが2.xに上がり、気づかないうちに新バージョンへ切り替わります。
私の結論は、移行を急ぐ必要はないものの、Pythonの依存に上限を入れる作業だけは今日やっておく、です。そのうえで影響箇所を洗い出し、SDKの更新と修正を一度のリリースにまとめます。洗い出しには、後半に載せる標準ライブラリだけの検査スクリプトを使えます。
3年ぶりの新バージョン。400になる送り方は3つ
Anthropicは10月21日(米国時間)、2023-06-01以来となるAPIバージョン2026-10-21をリリースノートで公開しました。公式の移行ガイドによると、Messages APIで次の送り方が400エラーになります。トークン数の計測と、Message Batchesの各リクエストも対象です。
- temperature、top_p、top_kのいずれかを送る。既定値と同じ値でも拒否される
- assistantメッセージの直下にdocumentブロックを置く。documentはuserメッセージへ移す
- userメッセージのtextブロックに、空でないcitationsを付ける
Admin APIとEnterprise Analytics APIでは、古いフィールド名が返らなくなりました。たとえばAPIキーのworkspace_idはscopeに置き換わり、Default Workspaceのキーもscope.workspace_idに実際のIDが入ります。workspace_idがnullならDefault Workspace、と判定していた管理スクリプトは書き換えが必要です。
2023-06-01は今も使えます。バージョン一覧は最新版の利用を勧めていますが、10月23日に確認した時点では、移行ガイドとバージョン一覧のどちらにも2023-06-01の廃止日は書かれていません。
切り替わるのはSDKのメジャー版を上げたとき。Pythonは知らないうちに上がりやすい
SDKは、そのメジャー版が出た時点のAPIバージョンを送ります。古いSDKを使い続ける限り、リクエストは2023-06-01のままです。新バージョンを送る最初の版は次のとおりです。
| 言語 | 最初の版 | 上げるときの注意 |
|---|---|---|
| Python | anthropic 2.0.0 | ロックなしで下限だけの指定なら、次のインストールで入る |
| TypeScript | @anthropic-ai/sdk 1.0.0 | bedrock-sdkやvertex-sdkなどの周辺パッケージも同時に1.xへ |
| Go | v2.0.0 | import pathに/v2が付く。Go 1.25以上が必要 |
| Java | anthropic-java 3.0.0 | MessageParam.builder()が廃止 |
| C# | Anthropic 13.0.0 | IChatClient経由のTemperature指定は無視される |
| PHP | anthropic-ai/sdk 1.0.0 | composer updateだけでは今の制約の範囲に留まる |
| Ruby | anthropic 2.0.0 | MessageParamまわりの型が変わる |
| ant CLI | 2.0.0 | --api-keyフラグが廃止。--api-key-stdinや環境変数を使う |
勝手に切り替わりにくいのは、Go、PHP、TypeScriptです。Goはimport pathを書き換えない限り上がりません。PHPはcomposerの制約の範囲に留まります。TypeScriptの旧版は0.x系で、npmのキャレット指定では^0.137.0が0.138.0にすら上がらないため、package.jsonがこの形なら1.0.0は入りません。
注意が要るのはPythonです。PyPIでは10月21日に2.0.0が公開され、同日中に2.1.0も出ました。anthropic>=1.0のような下限だけの指定や、バージョン無指定のrequirements.txtでは、ロックファイルのないDockerビルドやCIの新規インストールで2.1.0が入ります。その時点からリクエストは2026-10-21になり、temperatureを送る処理は400で止まります。
2023-06-01に留まるなら、上限を書いておくのが確実です。
anthropic>=1.17,<2
~=1.17でも2.0には上がりません。uvやPoetryのロックファイルがあれば、ロックを更新するまでは今の版のままです。依存の更新を自動で取り込む運用だと怖さは増します。LiteLLMの改ざん事件でも、バージョンを固定せずにpipで入れた環境が改ざん版を受け取りました。
temperatureを下げていた処理には、代わりの設定がない
引数を消せば400は止まります。問題は、消したあとの出力です。
移行ガイドによると、3つの引数を省いたリクエストは既定値(temperatureは1.0)で生成されます。Claude 4.7以降のように、もともと既定値しか受け付けないモデルなら挙動は変わりません。一方、Claude Sonnet 4.6のように別の値も受け付けるモデルで、出力をそろえる目的でtemperatureを0.2などに下げていた処理は、その効果を失います。代わりになる設定はありません。
移行ガイドが示す選択肢は、引数を送るのをやめるか、そのリクエストだけ2023-06-01に残すかの2つです。分類やタグ付けのように、同じ入力から同じ答えが返ることを前提にしていた処理は、消す前と後で同じ評価データを流して違いを確かめてください。私は、温度でそろえていた処理は、出力の形式を固定して結果を検証する方向へ寄せるのが本筋だと見ています。Claude 4.7以降はすでに既定値以外のtemperatureを受け付けず、温度に頼る設計は古いモデルを使い続ける理由になってしまうからです。
移行前のコードと移行後のコードの差は、引数1つです。
# anthropic 1.x(2023-06-01)
client.messages.create(model="claude-sonnet-4-6", max_tokens=512,
temperature=0.2, messages=messages)
# anthropic 2.x(2026-10-21):temperatureを送ると400。SDKの型からも消えた
client.messages.create(model="claude-sonnet-4-6", max_tokens=512,
messages=messages)
冒頭のissueから学べることがもう1つあります。400はコードの誤りであって、待てば直る障害ではありません。4xxを再試行や「一時的な障害」として扱っているなら、移行の前に通知が飛ぶように直しておくと、止まったことにすぐ気づけます。
APIキーの送り方がBearerに変わる。プロキシとテストのスタブを確認
新しいSDKとCLIは、APIキーをX-Api-KeyではなくAuthorization: Bearerヘッダーで送ります。Claude API自体はどちらでも受け付けるので、直接呼んでいる分には何も起きません。
壊れるのは、途中でX-Api-Keyを読んでいるものです。社内のAPIゲートウェイやプロキシ、キーをログから伏せるフィルター、テストで使うHTTPのスタブが当てはまります。ゲートウェイ側をAuthorizationに対応させるのが筋ですが、すぐ直せない場合のために、移行ガイドはPython向けにヘッダーを戻すコードを載せています。
import httpx2
from anthropic import Anthropic, DefaultHttpxClient
def send_x_api_key(request: httpx2.Request) -> httpx2.Request:
if request.headers.get("Authorization") == f"Bearer {api_key}":
del request.headers["Authorization"]
request.headers["X-Api-Key"] = api_key
return request
client = Anthropic(api_key=api_key, http_client=DefaultHttpxClient(auth=send_x_api_key))
細かい落とし穴もあります。Python版でextra_headers={"X-Api-Key": 別のキー}と書いていたコードは、キーの差し替えになりません。元のキーと両方が送られます。また、api_keyとauth_tokenのように認証情報を2つ渡すと、v2ではValueErrorになります。ヘッダーまわりを触るついでに、キーが漏れたときの止め方も見直すなら、APIキーの失効手順と支出上限の記事が役に立ちます。
影響箇所を洗い出す検査スクリプト
移行ガイドの変更点のうち、行単位で見つけられるものを探すスクリプトを書きました。Python 3.10以上の標準ライブラリだけで動き、外部パッケージは使いません。anthropicまたはclaudeという語を含むファイルだけを読み、node_modulesや.venvは飛ばします。
"""Claude API 2026-10-21で影響を受けそうな箇所を行単位で探す(標準ライブラリのみ)。"""
import re
import sys
from pathlib import Path
SKIP_DIRS = {".git", "node_modules", ".venv", "venv", "dist", "build"}
CODE = {".py", ".ts", ".js", ".mjs", ".go", ".java", ".rb", ".php", ".cs"}
RULES = [
("サンプリング引数(400エラー)", CODE, r"\b(temperature|top_p|top_k|topP|topK)\b"),
("X-Api-Keyヘッダー(新SDKはBearer)", CODE | {".yaml", ".yml", ".conf"}, r"(?i)x-api-key"),
("旧バージョンの明示指定", CODE | {".yaml", ".yml"}, r"2023-06-01"),
("MessageParam(...)の呼出し(Python v2でTypeError)", {".py"}, r"\bMessageParam\("),
("非betaのmodelsにbetas(v2で削除)", CODE, r"\bmodels\.(list|retrieve)\(.*betas"),
("上限なしの依存指定(次の導入でv2へ)", {".txt", ".toml"}, r"^\s*\"?anthropic\"?\s*(>=[^<,\"]*)?\"?,?\s*$"),
]
def scan(root):
for path in Path(root).rglob("*"):
if not path.is_file() or SKIP_DIRS & set(path.parts):
continue
text = path.read_text(errors="ignore")
if "anthropic" not in text.lower() and "claude" not in text.lower():
continue
for no, line in enumerate(text.splitlines(), 1):
for label, suffixes, pattern in RULES:
if path.suffix in suffixes and re.search(pattern, line):
yield f"{path}:{no}: {label}: {line.strip()[:80]}"
if __name__ == "__main__":
hits = list(scan(sys.argv[1] if len(sys.argv) > 1 else "."))
print("\n".join(hits) or "該当なし")
sys.exit(1 if hits else 0)
リポジトリの直下で実行します。該当がなければ「該当なし」と表示して終了コード0、あれば該当行を表示して1を返すので、CIに組み込むこともできます。
python3 check_claude_api.py .
私の環境(macOS、Python 3.12.9)で、問題を仕込んだサンプルに対して実行した結果がこちらです。temperature、MessageParam(...)、models.list(betas=...)、anthropic>=1.0を書いたPythonのサンプルと、X-Api-Keyと2023-06-01を書いたゲートウェイ設定を用意しました。
fixture/app/requirements.txt:1: 上限なしの依存指定(次の導入でv2へ): anthropic>=1.0
fixture/app/summarize.py:5: MessageParam(...)の呼出し(Python v2でTypeError): msg = MessageParam(role="user", content="要約して")
fixture/app/summarize.py:7: サンプリング引数(400エラー): temperature=0.2, messages=[msg])
fixture/app/summarize.py:8: 非betaのmodelsにbetas(v2で削除): models = client.models.list(betas=["x"])
fixture/proxy/gateway.yaml:3: X-Api-Keyヘッダー(新SDKはBearer): auth_header: X-Api-Key
fixture/proxy/gateway.yaml:4: 旧バージョンの明示指定: anthropic_version: "2023-06-01"
仕込んだ6か所はすべて検出されました。問題のないClaudeのコードと、OpenAIだけを使うファイルにあるtemperatureは、どちらも検出されていません。依存指定は、anthropic(無指定)と>=1.0を検出し、>=1.2,<2、==1.17.0、~=1.17は検出しないことを確かめました。
限界もはっきりさせておきます。行単位の文字列検索なので、temperatureを別の設定ファイルに書いている場合は見逃します。OpenAIとClaudeを1つのファイルで呼んでいれば、OpenAI側のtemperatureも検出されます。assistant直下のdocumentや、user側のcitationsは検出できません。会話履歴を組み立てるコードは目で確認してください。
なお、SDKの新旧版を実際にインストールしてAPIを呼ぶ確認はしていません。400になる条件とBearerでの送信は、公式の移行ガイドの記述に基づいています。
移行するか、2023-06-01に留まるかの決め方
判断は、temperatureを既定値以外で使っているかどうかでほぼ決まります。
- temperature等を送っていない、または送っていても既定値しか受けないClaude 4.7以降のモデルだけを使っている。この場合は移行して問題ありません。引数を消し、SDKを上げ、テストを通して出します
- Sonnet 4.6などで、temperatureを下げて出力をそろえている。この処理だけはSDKを1.xに固定して残し、評価データで既定値の出力を確かめてから移行します
- Amazon BedrockでOpus 4.6以前のモデルを呼んでいる。Bedrockでは2026-10-21がClaude 4.7以降でしか使えないため、今のSDKのまま留まり、先にモデルを切り替えます
移行すると決めたら、手順は次のとおりです。
- 依存に上限がなければ、まず
anthropic>=1.17,<2のように上限を入れ、意図しない切り替わりを止める - 検査スクリプトで該当箇所を出し、temperature等の削除、ヘッダー、型の変更を直す
- 4xxを障害として握りつぶしていないか、エラー処理を見直す
- SDKを新しいメジャー版に上げる。TypeScriptは周辺パッケージも同時に上げる
- 本番と同じ種類のリクエストをテスト環境で流し、400が出ないこと、出力が許容範囲であることを確かめてから出す
Claude Codeを使っているなら、v2.1.307で追加された/claude-api api-versionがコードの移行を手伝ってくれます。私はまだ試していないので、使った場合も、変更の差分と上の手順の確認は人が見るのがよいと考えています。
今日やるのは、requirements.txtを開くことだけでいい
今回の変更は、使い方を誤ると静かに壊れる種類のものです。とくにPythonは、誰も更新したつもりがないのにビルドのたびに新しいSDKが入りえます。移行の作業そのものは急がなくてかまいません。
まずはClaude APIを呼んでいるリポジトリのrequirements.txtかpyproject.tomlを開き、anthropicに上限が書かれているかを見てください。書かれていなければ<2を足すだけで、移行の時期を自分で決められるようになります。そのあとで検査スクリプトを1回流せば、移行にかかる作業の量も見えてきます。

コメント