Cloudflare AI Gateway の REST API で、AI プロバイダが資格情報を拒否したときの応答が揃った。対象は POST /ai/run だ。2026年10月6日付の changelog によると、ElevenLabs、Google Vertex、その他のプロバイダは、いずれも HTTP 401 とエラーコード 2009 を返すようになった。例外は Unified Billing を使っている場合で、こちらは HTTP 503 になる。
直す必要があるのは、ステータスコードでプロバイダ鍵の失敗を判定しているアプリだ。ElevenLabs の 403、Vertex の 500、その他のプロバイダの 402 を「鍵の問題」として拾っていた分岐は、変更後はどれにも当たらない。changelog も、REST API のエラーを扱うアプリは HTTP 401 を「無効または拒否されたプロバイダ資格情報」として扱うよう更新すべきだと書いている。
プロバイダごとの新旧レスポンス
| 状況 | 以前の応答 | 新しい応答 |
|---|
| ElevenLabs | プロバイダ固有の UserCredentialsError(HTTP 403) | HTTP 401、エラーコード 2009 |
| Google Vertex | 資格情報拒否で HTTP 500、上流への再試行あり | HTTP 401、エラーコード 2009。プロバイダへの再試行なしで失敗 |
| その他すべてのプロバイダ | HTTP 402 またはプロバイダ固有のステータス | HTTP 401、エラーコード 2009 |
| Unified Billing 利用時にプロバイダが資格情報を拒否 | プロバイダ固有の認証エラー | HTTP 503 |
出典:Cloudflare Changelog「Standardize provider credential error responses in AI Gateway」(2026年10月6日)
上の三行は 401 に収束した。プロバイダごとにばらばらだった 403・500・402 を個別に覚えておく必要はなくなる。ただし最後の行だけは 401 にならない。この非対称が、エラー処理を直すときのいちばんの落とし穴になる。
Vertex の鍵エラーは 5xx から外れる
以前の Vertex は、資格情報が拒否されても HTTP 500 を返し、しかも AI Gateway が上流への再試行をかけていた。鍵が間違っているだけなのに、アプリからはサーバー側の障害に見えていたことになる。
変更後は 401 とエラーコード 2009 が返り、リクエストはプロバイダへ再試行せずに失敗する。5xx を一時障害とみなして再試行する実装なら、Vertex の鍵エラーはその再試行の対象から外れる。エラー率を 5xx で監視している場合も、鍵の誤りが原因だった 500 は 4xx 側に数えられるようになる。Vertex を使っていて 500 の件数をアラートの閾値にしているなら、変更前後で数字の意味が変わる点は押さえておきたい。
Unified Billing では 401 にならない
Unified Billing を使っているときにプロバイダが資格情報を拒否すると、新しい応答は HTTP 503 だ。以前はプロバイダ固有の認証エラーが返っていた。
503 は一般に「一時的に使えない」という意味で扱われ、指数バックオフ付きの再試行対象に入れている実装は多い。そうした実装では、Unified Billing 経由の資格情報拒否も再試行のループに入る。changelog の表には、この 503 にエラーコード 2009 が付くかどうかは書かれていない。ほかの理由による 503 とどう見分けるかも、表からは読み取れない。Unified Billing と自前の鍵(BYOK)を併用しているアプリは、同じ「鍵の拒否」が経路によって 401 と 503 に分かれることになる。AI Gateway の請求まわりの変更は 月次請求の集約の記事 で扱った。
エラー処理を直す順番
既存のハンドラを次の順に見直すと漏れが少ない。
- 403・402・500 でプロバイダ鍵の失敗を判定している分岐と、
UserCredentialsError という名前で分岐している箇所を探す。
- HTTP 401 を「プロバイダ資格情報が無効または拒否された」として扱う分岐を加え、鍵の更新や設定確認に回す。
- エラーコード 2009 で絞り込みたい場合は、実際のレスポンスでコードがどこに入るかを確かめてから条件を書く。changelog の表はコードの値だけを示している。
- Unified Billing を使っているなら、503 の再試行回数と上限を見直す。
- Vertex の 5xx を監視している場合は、閾値とダッシュボードの前提を更新する。
ステータスだけで分ける最小の形は次のようになる。レスポンス本文の構造は changelog に載っていないので、ここでは触れていない。
const res = await fetch(gatewayUrl, { method: "POST", headers, body });
if (res.status === 401) {
// Provider rejected the credential (error code 2009 per the changelog)
throw new ProviderCredentialError(await res.text());
}
if (res.status === 503 && usesUnifiedBilling) {
// With Unified Billing, a rejected credential now surfaces as 503
// Keep retries bounded so a bad credential does not loop forever
}
changelog に書かれていないこと
対象として明記されているのは POST /ai/run だけだ。それ以外のエンドポイントで同じ変更が入ったかどうかは、changelog からは分からない。段階的な展開か一斉適用か、適用の時刻も書かれていない。401 のレスポンス本文の形式と、Unified Billing の 503 を他の 503 と見分ける手がかりも示されていない。ハンドラを書き換える前に、ステージング環境でわざと無効な鍵を送り、実際の本文を一度見ておくと安全だ。