Compliance APIのエラー400/403/404への対処法
Compliance APIが返す400/401/403/404などのエラーを原因と対処法つきで整理し、リトライ判断の基準をまとめます。
なぜCompliance APIのエラー処理を個別に設計する必要があるか
Compliance APIは標準的なAnthropicのエラー形式(非2xxのステータスコード・request-idレスポンスヘッダー・error.typeとerror.messageを含むJSONボディ)に従います。ただし、どのerror.typeがどう振る舞うかは、ChatやActivity Feed、ローカルセッションのようにエンドポイント群ごとに癖があります。同じnot_found_errorでも、チャットの404は「もう存在しない」を意味しますが、ローカルセッションの404の一部は「一時的に使えない」を意味します。ステータスコードだけを見てリトライ判定を書くと、この違いを踏み外します。
判定の基本はerror.typeで行い、メッセージ文字列そのものでは分岐しません。メッセージは運用ランブックに転記できる程度に安定していますが、言い回しが変わることがあります。error.typeはAPI契約の一部です。ただし、ローカルセッションの一部エンドポイントだけは同じtypeを複数のメッセージで使い分けているため、その箇所だけはメッセージ文字列での判定が要ります。
ステータス別のリトライ可否
| ステータス | リトライ | 状況 |
|---|---|---|
| 400 Bad Request | リトライしない | 状況パラメータを直して再送 |
| 401 Unauthorized | リトライしない | 状況キーを直すかローテートして再送 |
| 403 Forbidden | リトライしない | 状況不足スコープを足すか、キー種別を変えて再送 |
| 404 Not Found | リトライ基本しない | 状況リソースが削除済みか未作成。キューから除去 |
| 409 Conflict | リトライしない | 状況状態の競合を解消してから再送 |
| 429 Too Many Requests | リトライする(retry-after後) | 状況待機してから再送、カーソルは進めない |
| 500 / 502 / 503 / 504 / 529 | リトライヘッダー次第 | 状況x-should-retryを確認してから判断 |
400 Bad Request — パラメータの形式不備
リクエスト自体は構文的に正しいものの、サーバーが拒否した値を含んでいるケースです。
タイムスタンプ関連では、created_at.gteのようなパラメータがRFC 3339形式でパースできないとinvalid_request_errorが返ります。エラーメッセージには失敗したパラメータ名と送信値がそのまま含まれるので、2024-03-01T00:00:00Zのように時刻とタイムゾーンを必ず含めて送り直します。ローカルセッション一覧では、created_at.ltがcreated_at.gteより後になっていないと同種の400が返ります。
limitパラメータが範囲外のときも同じinvalid_request_errorです。上限はエンドポイントごとに異なるため、APIリファレンス側のパラメータ制約を確認してから値を決めます。セッション本文の切り詰めを制御するtool_use_input_max_bytesとtool_result_max_bytesも、正の整数か-1(サーバー上限を使う指定)以外を送ると同じエラー型になります。
ページネーションカーソル(after_id / before_id / page)が壊れているときの400もよくあります。カーソルは常に前回レスポンスの値をそのまま渡し、自分でオブジェクトIDから組み立てないのが対処の基本です。ローカルセッションのメッセージ取得エンドポイントでは、pageカーソルがセッションIDとソート順にひもづいており、別のセッションや別の並び順で使い回すと「このリクエストに対して有効なカーソルではない」というエラーになります。さらにこのカーソルは発行から24時間で失効するため、長時間放置したウォークはpageなしで再開する必要があります。
401 Unauthorized — キーそのものが無効
x-api-keyヘッダーが欠落しているか、既知のキーと一致しない場合です。authentication_error型で、メッセージは「キーが無効か失効している」という一文に固定されています。ヘッダーが空でも同じ本文が返るため、シークレットストアの値とキーの失効状態の両方を確認します。スコープ不足は401ではなく403として区別されるので、401が出た時点でスコープの心配はしなくてよい、というのも判断を早めるポイントです。
403 Forbidden — スコープまたはキー種別の不一致
permission_error型で、メッセージにGot:(キーが持つスコープ)とNeeded:(エンドポイントが要求するスコープ)が具体的に列挙されます。ここを読むだけでConsoleを開き直さずに原因を特定できます。Complianceアクセスキーのスコープは作成後に変更できないため、403のfixは基本的に「スコープを足した新しいキーを作る」一択です。
主なパターンを実際のエラーボディで示します。
{
"error": {
"type": "permission_error",
"message": "Missing required scopes. Got: ['read:compliance_user_data'] Needed: ['read:compliance_org_data']"
}
}Admin APIキー(sk-ant-admin01-...)はread:compliance_activitiesしか持てないため、Activity Feed以外のあらゆるエンドポイントで403になります。組織・ロール・グループ・実効設定へのアクセスにはComplianceアクセスキーのread:compliance_org_dataが、チャット・ファイル・プロジェクト・セッション・ユーザー・グループメンバーにはread:compliance_user_dataが要ります。削除系エンドポイントだけはさらにdelete:compliance_user_dataが別枠で必要です。この使い分けの詳細はCompliance APIで組織・ユーザー・ロール・グループ一覧を取得するにまとめています。
read:compliance_org_settingsという廃止済みスコープしか持たないキーで実効組織設定エンドポイントを呼ぶと、同じ403の型で「このスコープはもう何も許可しません」という状態になります。すでに動いていた連携が急に403を返し始めたら、このスコープ廃止が原因になっていないか真っ先に疑います。
親組織を持たない単独のClaude Console組織は、そもそもComplianceアクセスキーを作れません。Activity Feed以外のエンドポイントを前提にした設計は、この組織形態では成立しない点も覚えておきます。
404 Not Found — リソースの不在、ただし例外あり
エンドポイントは解決できたものの、指定したIDのリソースが存在しないか、すでに削除済みのケースです。Compliance API経由の削除は即時かつ恒久的なので、以前は取得できていたIDが404になった場合、多くはハード削除か保持ポリシーによる失効です。
チャット・ファイル・プロジェクトの404は素直な「不在」ですが、セッション系には2つの例外があります。ローカルセッションのエンドポイントは、IDに関係なくLocal sessions are not available.という本文を返すことがあり、これは親組織に対してその機能自体が一時的に使えない状態を意味します。個別のセッションが存在しない場合のLocal session not found.とはerror.typeがどちらもnot_found_errorで同じなので、メッセージ文字列で見分ける必要がある数少ない箇所です。リモートセッションでは、statusがpendingのセッションはトランスクリプトがまだ無いため、開始するまでメッセージ取得エンドポイントが404を返し続けます。
実効組織設定の404も特殊です。存在しない組織ID・UUID形式でない値・親組織の対象外・エンドポイント自体が未有効化、という4つの原因がすべて同じボディを返すよう意図的に設計されています。これは「その組織IDが実在するかどうか」を404のレスポンスから読み取れないようにするためです。
409 Conflict — 状態の競合
プロジェクトにチャットが紐づいたまま削除しようとすると、conflict_error型でチャットを先に切り離すよう促すエラーが返ります。子リソースを先に処理してから親を削除する、という順序をコード側で強制するとこの409を避けられます。
429 Too Many Requests — レート制限
Compliance APIは親組織単位で毎分600リクエストの共有予算です。Complianceアクセスキーと連結組織すべてのAdmin APIキーがこの予算を分け合い、/v1/compliance/*の全エンドポイントに対して1つの枠になります。リモートセッション系のエンドポイントだけは、この共有予算の上にさらに専用の第二の予算を持ちます。
{
"error": {
"type": "rate_limit_error",
"message": "Compliance API rate limit of 600 requests per minute per parent organization has been exceeded. Retry after the time indicated by the retry-after header. Quote the request-id response header when contacting Anthropic support."
}
}レスポンスにはanthropic-ratelimit-requests-limit / -remaining / -resetという標準のレート制限ヘッダーが載るので、429を待つのではなく-remainingを見て事前に速度を落とせます。429自体にはretry-after秒数が入り、この値を尊重して待ちます。429が返ったリクエストはページネーションのカーソルを進めないのが鉄則です。失敗したリクエストはデータを返していないため、最後に成功したページのカーソルがそのまま次回も正しい値です。
認証に失敗したリクエスト(キーが無い・不明・Claude APIキーの誤用)はレート制限の手前で弾かれ、クォータを消費しません。一方でスコープ不足による403は、判定の前に1クォータを消費してから返ります。
500 / 502 / 503 / 504 / 529 — サーバー側のエラー
500にはx-should-retry: falseヘッダーが付くことがあり、これは決定論的な失敗(何度再送しても同じ結果になる)を示します。汎用のHTTPリトライライブラリを使っている場合は、このヘッダーがある500ではリトライを止める設定が要ります。ヘッダーがない500と、502・503・504・529は基本的に一時的なので、1秒から始めて最大60秒まで倍々に増やす指数バックオフで再送します。
例外はローカルセッションの一部の503です。overloaded_error型で本文が3種類あり、負荷起因の一時的なもの(index is temporarily unavailable)、顧客管理鍵(customer-managed encryption key)が使えない間ずっと続くもの(Captured content is temporarily unavailable)、保持設定の評価待ち(retention overrides)に分かれます。error.typeだけでは区別できないため、このケースは本文の文字列を見て挙動を変える必要があります。顧客管理鍵が原因なら、鍵のステータスを鍵管理サービス側で確認するまでリトライを続けても解消しません。
エラー対応をどう設計に落とし込むか
Compliance APIのエラー処理で実務上の分岐点になるのは、「error.typeだけで足りる箇所」と「メッセージ文字列まで見る必要がある箇所」が混在していることです。大半のエンドポイントはerror.typeだけで十分ですが、ローカルセッションの404と503だけは例外的にメッセージ本文を条件分岐に使います。この2か所を最初から特別扱いしてコードに組み込んでおくと、後から挙動の違いに気づいて書き直す手戻りを避けられます。
エラー処理を含めた統合設計の全体像や、Compliance API自体の有効化・スコープ設計はCompliance APIのFAQに、組織・ユーザー・ロール・グループの具体的な取得手順はCompliance APIで組織・ユーザー・ロール・グループ一覧を取得するにまとめています。
まとめ
Compliance APIのエラー判定はerror.typeが基本ですが、ローカルセッションの404と503だけはメッセージ文字列まで見る例外です。429ではカーソルを進めずretry-afterを尊重し、500系はx-should-retryヘッダーの有無でリトライ可否を分けます。403のメッセージに現れるGot: / Needed:を読めば、Consoleを開き直さずにキーの作り直しが必要かどうか判断できます。