APIが400 Bad Requestと{"error": "invalid input"}というボディを返した場合。人間の開発者はドキュメントを開き、ペイロードを確認し、欠落しているフィールドを見つけて、1分で修正します。エージェントは同じ2つの単語を読み取っても、何も行動できず、できる唯一のこと、つまり同じリクエストを再度送信します。そしてまた。その後、エージェントは諦め、ユーザーにAPIが壊れていると伝えます。
エラーレスポンスは、エージェントが最も依存するAPIの一部であり、チームが最後に設計する部分です。優れたエラーは、呼び出し元に何が問題だったのか、再試行が役立つか、何を変更すべきかを伝えます。エージェントはこれら3つすべてに対応できます。曖昧なエラーは、回復可能な問題を失敗したタスクに変えてしまいます。
このガイドは、関係のAPI側について書かれています。エージェントのエラー回復に関する当社の記事では、クライアントが再試行、バックオフ、サーキットブレーカーで何をすべきかを説明しています。このガイドでは、クライアント側のロジックが機能するためにAPIが何を返す必要があるかを説明しています。
ほとんどのAPIにおいてエラーレスポンスが最もテストされていない部分であるため、ここではApidogが重要になります。エラーレスポンスは、仕様で定義し、モックし、ハッピーパスをテストするのと同じ場所でアサートできます。
エラーが答えるべき3つの質問
エージェントが受け取るすべてのエラーレスポンスは、推測することなく3つの質問に答えられるようにすべきです。
これは私のせいですか、それともあなたのせいですか?4xxはリクエストが間違っており、変更せずに繰り返しても再び失敗することを意味します。5xxはサーバー側で何らかの問題が発生しており、同じリクエストでも後で成功する可能性があることを意味します。これらを区別できないエージェントは、バリデーションエラーで永遠に再試行するか、一時的な障害で諦めてしまいます。
再試行すべきですか、いつですか?4xxエラーの中には再試行可能なものとそうでないものがあります。429は待機後に再試行可能です。409は状態を再読み込みした後に再試行可能かもしれません。422はペイロードを変更しなければ再試行できません。どちらなのかを明示的に伝えてください。
具体的に何を修正すればよいですか?これはほとんどのAPIが省略するフィールドです。「バリデーション失敗」は役に立ちません。「countryがUSの場合、フィールドcustomer.postal_codeが必要です」という情報であれば、エージェントは次の試行で修正を適用できます。
これらの3つの情報をすべてのエラーに含めれば、ほとんどのエージェントの再試行の嵐は消滅するでしょう。
構造化されたエラーフォーマットを使用する
独自の形式を発明しないでください。RFC 9457、HTTP APIのProblem Detailsがそれを定義しており、広くサポートされています。
{
"type": "https://api.example.com/errors/validation-failed",
"title": "Validation failed",
"status": 422,
"detail": "The field 'customer.postal_code' is required when 'country' is 'US'.",
"instance": "/v1/orders",
"errors": [
{
"field": "customer.postal_code",
"code": "required_conditional",
"message": "Required when country is US. Provide a 5-digit or 9-digit US postal code.",
"example": "94107"
}
],
"retryable": false,
"next_action": "Add customer.postal_code to the request body and send again."
}
エージェントにとって重要な4つの部分があります。
detailは、実際のエージェントと実際のルールを記述した完全な文です。カテゴリではありません。このリクエストで失敗した具体的な内容です。
errors配列は機械で読み取り可能で、問題ごとに1つのエントリがあり、エージェントが送信したペイロードにマッピングできるフィールドパスが含まれています。すべての失敗を一度に返してください。一度に1つずつ返すと、単一の修正が5回のラウンドトリップになってしまいます。
retryableはブール値であり、ステータスコードから推測するものではありません。これはエージェントを最も助ける拡張機能であり、1つのフィールドで済みます。
next_actionは平易な指示テキストです。モデルはエラーコードから推論するよりも、レスポンスボディ内の明示的な指示に従う方が信頼性が高く、ここにある1つの文が、しばしば失敗したタスクを完了したタスクに変えます。
GoogleのAPIエラー設計ガイドは、異なる方向から同様の結論に達しており、特にエラーの詳細は散文ではなく構造化されたリストに含めるべきだと述べています。
いつ戻ってくるべきかを伝える
一時的なものに対しては、いつ行うべきかを伝えてください。30秒待つことを知っているエージェントは30秒待ちます。知らないエージェントは何かを選択し、それは通常短すぎます。
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json
{
"type": "https://api.example.com/errors/rate-limited",
"title": "Rate limit exceeded",
"status": 429,
"detail": "You have used 1000 of 1000 requests in the current minute window.",
"retryable": true,
"retry_after_seconds": 30,
"next_action": "Wait 30 seconds before sending this request again. Do not retry sooner."
}
Retry-Afterヘッダーは、秒単位の遅延またはHTTP日付を受け入れます。秒単位の方がクライアントが対応しやすいです。標準クライアントにはヘッダーとして送信し、モデルにはボディ内で繰り返してください。重複は安価であり、両方のコンシューマが最も読みやすい形式で情報を受け取ります。レート制限の詳細は、サーバー側の場合、レート制限超過ガイドおよびAPIレート制限の実装方法で説明されています。
同じパターンは、メンテナンス中の503やロックされたリソースに対する409にも適用されます。待機が正しい応答であるエラーには、数値を含めるべきです。
内部情報を決して漏らさず、何も返さないこともない
2つの障害モードは正反対の極端に位置し、どちらもエージェントに害を与えます。
1つ目はスタックトレースです。内部例外テキストを返すことは、フレームワークのバージョン、ファイルパス、場合によってはクエリの断片を露呈させます。これはエージェントの問題である以前にセキュリティ上の問題であり、信頼できない入力に対するAPIテストに関する当社の記事の懸念事項が直接適用されます。また、モデルがアクションを起こせないテキストでコンテキストウィンドウをあふれさせてしまいます。
2つ目は空のエラーです。ボディがない500、または{"error": true}です。エージェントは何も学べず、再試行するか終了するかのどちらかの選択肢しかありません。
中間的な解決策は、相関IDを持つ安定した公開エラーです。
{
"type": "https://api.example.com/errors/internal",
"title": "Internal error",
"status": 500,
"detail": "The order could not be created due to an internal error. No order was created.",
"retryable": true,
"retry_after_seconds": 5,
"request_id": "req_01J8ZK3M2Q",
"next_action": "Retry once after 5 seconds. If it fails again, stop and report request_id req_01J8ZK3M2Q."
}
「注文は作成されませんでした」という文は最も価値のある部分です。曖昧な書き込みに直面したエージェントは、再試行が重複のリスクを伴うかどうかを判断しなければならず、ほとんどは誤った判断を下します。現在の状態を伝えてください。それが保証できない場合は、操作を冪等にしてその旨を伝えてください。これはAIエージェントの冪等性キーに関する当社の記事のパターンです。
request_idは、人間が最終的にトランスクリプトを読んだときに、ログへのスレッドバックを提供します。IDが実際に何かに解決されるように、API監視ガイドのプラクティスと組み合わせてください。
エラーは仕様に属する
エラーの形式がOpenAPIドキュメントにない場合、生成されたクライアント、モック、エージェントツールに関する限り、それは存在しないことになります。ほとんどの仕様は200を詳細に記述し、その他すべてについては曖昧に扱います。
responses:
'201':
description: Order created
content:
application/json:
schema: { $ref: '#/components/schemas/Order' }
'422':
description: >
Validation failed. Not retryable without changing the request body.
The errors array names each invalid field.
content:
application/problem+json:
schema: { $ref: '#/components/schemas/Problem' }
'429':
description: >
Rate limited. Retryable. Wait for retry_after_seconds before sending again.
content:
application/problem+json:
schema: { $ref: '#/components/schemas/Problem' }
これらの記述は単なる装飾ではありません。OpenAPI仕様をエージェントツールに変えるためのガイドにあるように、仕様からエージェントツールを生成するとき、そのテキストはモデルが失敗ケースについて読み取る情報になります。「再試行可能、まず待機」と書かれた記述は、「リクエストが多すぎます」と書かれたものよりも良い挙動を生み出します。
成功だけでなく、エラーもテストする
エラーパスは、トリガーするのに労力がかかるため、テストカバレッジが崩壊する場所です。モックは労力を取り除きます。

APIプロジェクトで各エラーレスポンスを定義し、それをモックすることで、エージェントがオンデマンドですべてのケースに対応できるようにします。Apidogでは、エンドポイント定義に失敗レスポンスを追加し、それらの間でモックを切り替えることができます。これにより、実際のものを壊すことなく、エージェントを422、429、500に対して繰り返し実行できる方法を提供します。AIエージェントを本番ではなくモックAPIに対して実行するに関する当社の記事は、より広範な習慣をカバーしています。
- 複数の不正なフィールドによるバリデーション失敗。すべての問題が1つのレスポンスで返され、エージェントの次の試行が1つだけでなくすべての問題を修正することを確認します。
- 待機を伴うレート制限。エージェントが連続してリクエストを送るのではなく、少なくとも
retry_after_secondsの間待機することを確認します。 - 書き込み時のサーバーエラー。エージェントが再試行時にサイレントに重複を作成しないことを確認します。
- 認証失敗。待機しても不正なトークンは修正されないため、エージェントが再試行するのではなく停止することを確認します。エージェント向け最小権限APIキーに関する当社の記事では、認証情報側について説明しています。
- 不正なエラーボディ。有効なJSONではないものを返し、エージェントが正常に機能低下することを確認します。最終的には、アップストリームプロキシがこれを引き起こします。
これらのセットをシナリオとして保存し、CIで実行されるようにします。エラー処理は静かに退化することがあり、通常は誰かがシリアライザをリファクタリングしたときに発生し、ハッピーパスのスイートでは気づかれません。
より良いエラーがもたらす価値
その価値は3つの箇所で現れ、一度見れば簡単に測定できます。
無駄な再試行の削減。{"error": "invalid input"}に直面したエージェントは、通常、同じペイロードを2、3回再試行してから終了します。各試行はモデルのターンと、コンテキストとしての完全な会話コストがかかります。欠落しているフィールドを特定するレスポンスは、通常、1回の修正された試行で済みます。これは、ルーティンのバリデーションエラーにおいて、4回の呼び出しと2回の呼び出しの差です。
エスカレーションの削減。回復できないエージェントは、タスクを人間に引き継ぎます。回避可能な引き継ぎはすべて、エージェントが防ぐべきだった高価な結果です。修正を特定するエラーは、自動化内で実行を維持します。
デバッグ時間の短縮。何かを人間が処理する必要がある場合、request_idと正確なdetailがあれば、ログを検索する手間が1回の検索に変わります。これは、API監視ガイドが相関関係について述べているのと同じ議論であり、実行が中断した瞬間に適用されます。
見落としがちな4番目の利点があります。同じ改善が人間の開発者にも役立ちます。エラーメッセージがどのフィールドが間違っているかについて具体的に過ぎると不平を言った人はいません。
エスカレーションも考慮した設計
一部のエラーは、エージェントによって本当に回復できません。スコープの欠落、閉鎖されたアカウント、人間の判断が必要なルールなどです。これらの場合、エラーの役割はクリーンに引き継ぐことです。何が起こったのか、人間が何をすべきか、そして引き継ぎを安価にする相関IDを伝えることです。
その返信は、人間が読む場所に届けられなければなりません。エージェントが割り当てられたタスクを処理するコーディングランタイムである場合、通常は周囲のプラットフォームに到達します。Sharklyは、エージェントの結果と実行トレースをタスク上に保持し、返信またはレビューが必要な項目をInboxにルーティングするため、ブロックされた実行はログ内の1行としてではなく、作業として可視化されます。「無効な入力」と読めるメッセージは、レビュアーにエージェントに与えた以上の情報を提供しないため、エラーテキストがその引き継ぎを役立つものにします。

エージェントに散文を解析させない
最後のアンチパターンは、有機的に成長したAPIでよく見られます。ステータスコードは正しいのですが、ボディは文になっており、個別の失敗ごとに異なる表現が使われています。
{ "message": "Sorry, that didn't work. Please check your details and try again." }
エージェントはこれを推測することによってのみ応答できます。さらに悪いことに、チームはしばしばこれを200ステータスと組み合わせるため、クライアントライブラリは失敗を認識さえしません。
2つのルールでこれを修正できます。個別の失敗ごとに安定した機械可読なコードを与え、エージェントが「資金不足」というフレーズではなくinsufficient_fundsで分岐できるようにします。そして、クライアント側の利便性がいかなるものであっても、成功ステータスコードで失敗を返してはいけません。エラーを含む200は、あらゆる再試行ポリシー、あらゆるダッシュボード、あらゆるアラートにとって不可視です。
エージェントが読み取れるエラーのチェックリスト
- すべてのエラーは、API全体で一貫した構造化フォーマットを使用します。
detailは特定のフィールドまたは条件を記述し、カテゴリではありません。- バリデーションエラーは、フィールドパスとともにすべての問題を一度に返します。
- すべてのエラーに
retryableブール値が含まれます。 - 再試行可能なエラーは、ヘッダーとボディに秒単位の待機時間を含みます。
- 書き込み失敗は、何かが作成されたか変更されたかを示します。
- すべてのエラーには、ログで解決される相関IDが含まれます。
- スタックトレース、フレームワーク文字列、SQLは含みません。
- エラーレスポンスは、エージェントが読み取れる説明とともに仕様に文書化されています。
- 各エラーのモックが存在し、保存されたテストがCIでそれらを実行します。
エラーはインターフェースです。実際にいる呼び出し元、つまりますます増えているのは、レスポンスボディが指示することを正確に行うモデルのために、エラーを設計してください。エージェントが実際にエラーに遭遇する前に、エラーの形式を定義し、モックするにはApidogをダウンロードしてください。
よくある質問
RFC 9457と独自のエラーフォーマットのどちらを使用すべきですか?既に本番環境で一貫したフォーマットがある場合を除き、RFC 9457を使用してください。一貫性は標準化よりも優先されます。エンドポイントの半分を新しい形式に切り替えるのは、すべての場所で1つの形式を維持するよりも悪いことです。どちらを使用する場合でも、retryableとnext_actionの拡張機能を追加してください。
next_actionテキストをAPIレスポンスに含めるのは安全ですか?はい、サービスが固定されたテンプレートセットからそれを生成する場合に安全です。ユーザーが提供したコンテンツをこのフィールドにエコーしてはなりません。エージェントはそれを指示として読み取り、プロンプトインジェクションの経路となるためです。信頼できない入力に対するAPIテストに関する当社の記事では、そのリスクについて説明しています。
バリデーションエラーは400と422のどちらにすべきですか?壊れたJSONなど、リクエストが不正な形式の場合は400を使用し、リクエストは解析できるがビジネスルールに失敗する場合は422を使用してください。エージェントは修正方法が異なるため、この区別から恩恵を受けます。すでに両方に1つを使用している場合は、変更するのではなく文書化してください。
どの程度の詳細が多すぎますか?呼び出し元が行動するのに十分な情報がある時点で止めてください。フィールド名、ルール、およびサンプル値で通常は十分です。内部識別子、クエリテキスト、スタックフレームは許容範囲を超えています。
エラーメッセージはコンテキストウィンドウにカウントされますか?はい、そして再試行を繰り返すたびに冗長なエラーがすぐに蓄積されます。数百トークン未満に抑えてください。エージェント向けAPIレスポンスのトリミングに関する当社の記事は、成功だけでなく失敗にも適用されます。
再試行不可能なエラーをエージェントが再試行するのをどのように止めますか?retryable: falseを設定し、next_actionでその旨を伝え、ツールラッパーで強制することで、モデルの判断が唯一のガードではないようにします。ここでは念には念を入れるのが正しいです。
