準拠レベル
セラーはエラーハンドリングを段階的に採用できます。各レベルは前のレベルの上に構築されます:
Level 1 は準拠実装の最低要件です。Level 2 でエージェント主導の復旧が可能になります —
recovery がなければエージェントはエラーコードから推測するしかありません。Level 3 で @adcp/client のようなクライアントライブラリが完全な型付きエラーオブジェクトを提供できます。
エラーの分類
1. プロトコルエラー
AdCP ビジネスロジック外の通信・接続問題:- ネットワークタイムアウト
- 接続拒否
- TLS/SSL エラー
- JSON パースエラー
2. タスクエラー
status: "failed" で返るビジネスロジックの失敗:
- 在庫不足
- 無効なターゲティング
- 予算バリデーション失敗
- リソース未検出
recovery フィールドを確認して、リトライするか、リクエストを修正するか、エスカレートするかを判断します。
3. バリデーションエラー
スキーマ検証に失敗する不正リクエスト:- 必須項目の欠落
- 無効な型
- 範囲外の値
エラーレスポンス形式
失敗した処理はステータスfailed とエラー詳細を返します。エラーオブジェクトは error.json スキーマに従います:
エンベロープ vs. ペイロードエラー — 二層モデル
AdCP はエラーを 2 つの異なる場所で公開し、実装者は状況に応じて正しい層を設定する必要があります。これはエージェントとストーリーボードの間でエラー形状のドリフトが起きる最も一般的な原因です。
致命的なタスク失敗は両方の層を設定すべきです(SHOULD)。 ペイロードは任意のプロトコルがそのまま読める構造化された
errors[] 配列を運び、トランスポートエンベロープは MCP/A2A クライアントがペイロードを再解析せずに型付きエラーを抽出できるよう adcp_error を運びます。2 つのうち片方だけを設定するのが、ほとんどの相互運用バグの原因です — トランスポートエンベロープを読むランナーはエラーを見ず、ペイロードを読むランナーはトランスポート上にエラーシグナルを見ません:
status: "submitted" または status: "input-required" タスク(例: 「このメディアバイは手動承認が必要です — [警告詳細]」)は、ペイロードの errors[] に severity: "warning" を設定しますが、adcp_error を設定してはなりません(MUST NOT)。トランスポートエンベロープは「タスクが失敗した」を示すものであり、警告チャネルではありません。
ストーリーボードバリデーター。 失敗したタスクのエラーコードをアサートする際は、check: field_present, path: "errors" ではなく check: error_code を優先してください。error_code は形状非依存です — ランナーは adcp_error.code(トランスポート)または errors[0].code(ペイロード)のいずれかから解決します。直接の path: "errors" チェックはアサーションをペイロード形状に固定し、エージェントがコンフォーマントであってもトランスポートエンベロープ経由でのみエラーを表面化するエージェントに対して失敗します。Storyboard authoring — Asserting on errorsを参照してください。
判別付き拒否アーム。 タスクレスポンスが構造化された拒否アーム(例: AcquireRightsRejected、CreativeRejected — ルールは GOVERNANCE_DENIED のワイヤー配置ガイダンスを参照)を定義する場合、スペック的に正しい拒否レスポンスはワイヤー上にエラーコードを運びません — 拒否アームはスキーマ層で not: { required: [errors] } を強制します。check: error_code のアサートはコンフォーマントなエージェントに対して失敗します。代わりに判別子でアサートしてください: check: field_value, path: "status", value: "rejected"。これは acquire_rights のガバナンス拒否と creative_approval のポリシー拒否のパターンです。2 つのパスを混在させるアサーション(拒否アームを持つタスクに error_code、持たないタスクに field_value)は、非スペックの見解をストーリーボードに焼き込みます。
エラーオブジェクトのフィールド
これらのフィールドはerror.json スキーマで定義されています:
Validator-internals フィールド(issues)
各 issues[] エントリの 3 つの任意フィールドは、ペイロードを拒否したスキーマ要素を名指しするため、エージェントはバリエーションを探る代わりに 1 回のイテレーションでバリデーションエラーから復旧できます:
schema_id の解決。 文字列はスキーマの $id です。スキーマを読み込むには:
- HTTPS 正準:
https://adcontextprotocol.orgを前置します(例:https://adcontextprotocol.org/schemas/3.1.0/core/activation-key.json)。キャッシュ可能。バージョンごとに不変。 - SDK バンドル:
@adcp/sdkとadcp-client-pythonはオフライン解決のためスキーマバンドルを同梱します — バンドルツリーに対して$idで検索します。 - バンドルツリーの注意点。 事前解決されたバンドルツリー(
/schemas/{version}/bundled/...)から提供されるツールは、$idを保持したままサブスキーマをインライン化します(3.1+、#3868 を参照)— ただしバンドル内の各サブスキーマの最初の出現でのみです。同じソーススキーマが複数の同居場所から参照される場合、$idは最初のインラインにのみ付与され、後続の出現は、SDK のエラーレポートがスキーマツリーを遡る際に最も近い$idを持つ祖先(通常はレスポンスルート)にフォールバックします。サブツリーに巻き上げられた$defs参照を含むサブスキーマも、$idを保持するとローカルフラグメント解決が壊れるため、バンドル時に$idが剥がされます。#3868 より前に生成されたバンドルを読むコンシューマーはレスポンスルートの$idのみを見ます。$idが/bundled/<tool>-response.jsonで終わるかを確認して pre-#3868 のケースを検出し、その場合はバンドルスキーマをpointerで辿ってフォールバックします。
oneOf / anyOf であり、(b) 判別子プロパティがペイロードに存在する場合にのみ discriminator を設定します。ワイヤーフィールドは呼び出し元が送った値を報告します — 部分一致ヒューリスティクスに基づくバリデーターの推論ではありません — ため、Ajv、Python jsonschema、gojsonschema にわたって決定論的です。生き残るバリアントがゼロの場合、セラーは discriminator を省略しなければなりません(MUST、省略が「バリデーターがターゲットバリアントを局所化できなかった」というエージェントへのシグナル)。複合判別子(例: audience-selector の (type, value_type))の場合、エントリは拒否したスキーマの properties ブロックでの宣言順に並びます。
判別子プロパティがペイロードから欠落している場合(バリデーターがブランチ選択すら開始できない)、セラーは discriminator を省略します — 復旧シグナルは、欠落した判別子プロパティを名指す keyword: "required" を持つ兄弟の issues[] エントリから来ます。「必須の判別子フィールドが欠落」+「この issue に discriminator なし」を読むエージェントは、名指されたプロパティを設定して復旧します。バリアントに一致しなかった値を持つ discriminator 配列を見るエージェントは、別の値に切り替えて復旧します。
本番発行ルール(公開スペックのスタンス)。 3 つのフィールドはすべて、拒否した要素がセラーが get_adcp_capabilities で宣伝するバージョンの公開スペックに存在する場合、本番で発行しても安全です。根拠は replay-locally です: スキーマは adcontextprotocol.org で公開され、すべての SDK にバンドルされているため、同じペイロードに対して同じバリデーターを実行する敵対者は同じブランチ選択を導出します — ワイヤーフィールドは彼らが計算できない情報を運びません。
replay-locally の議論には、セラーが順守しなければならないカーブアウトがあります:
- プライベート拡張。 カスタム
oneOfブランチ、サーバー専用のサブスキーマ、additionalProperties: trueで重ねた enum サブセットを実行するセラーは、拒否した要素が公開スペックに存在しない場合、schema_id、schemaPath、discriminatorを発行してはなりません(MUST NOT)。発行は、敵対者が再現できないセラー内部のバリデーション状態を漏らします。実装: 公開 + プライベートの混在バリデーションツリーを実行するセラーは通常、(a) 公開専用とプライベート専用のバリデーターを別々にコンパイルし公開ランからのみschema_idを発行するか、(b) 各$idをソースバンドルにマッピングするサイドテーブルでコンパイル済みスキーマを計装します。 - バージョンスキュー。 プレリリースまたはポストリリースのスキーマに対して検証するセラーは、
get_adcp_capabilitiesで名指したバージョンの公開バンドルに存在しない$idを持つschema_idを発行してはなりません(MUST NOT)。 - サーバー狭窄化された公開要素。 セラーがサーバー側で公開 enum、パターン、数値範囲をテナント固有のサブセットに絞る場合(例: 公開スキーマでは
["a","b","c"]を受け入れるが、この呼び出し元には["a"]以外をすべて拒否)、セラーは公開schema_idに対してkeyword: "enum"(またはpattern/minimum/maximum)でVALIDATION_ERRORを返してはなりません(MUST NOT)。公開スペックの再現は値を受け入れますが、セラーはプライベート状態で拒否します。拒否が公開スキーマ要素に誤帰属されないよう、代わりにPOLICY_VIOLATIONまたはUNSUPPORTED_FEATUREを使用します。「公開再現は受け入れ、セラーは拒否」の構造的差分自体が、セラーのプライベートサブセットの指紋です。 - カスタムキーワード。
keywordは JSON Schema Draft 7 / 2020-12 の語彙から取らなければなりません(MUST)— バリデーター固有のカスタムキーワード(AjvaddKeyword、instanceof)を使うセラーはそれらをワイヤーに発行してはなりません(MUST NOT)。 - プローブの簡潔性。 セラーは、上記のカーブアウトが適用されない場合でも、本番エンベロープを簡潔に保つため、これら 3 つのフィールドをレート制限されたエンドポイントの dev/sandbox レスポンスにスコープしてもよい(MAY)。フィールドの省略は常にコンフォーマントです。
標準エラーコード
標準エラーコードはerror-code.json で定義されています。語彙はオープンです: error.code はワイヤー上 string として型付けされ、標準コードは文書的で、送信者は標準セット外のコードを発行してもよい(MAY)。
Forward-compatible decoding(規範的)
エラーコード語彙はオープンです。error.code は core/error.json で string として型付けされています — 閉じた enum ではありません — ため、厳格な JSON Schema バリデーターは任意の文字列値を受け入れなければなりません(MUST)。error-code.json の標準語彙は文書的であり、ワイヤーレベルで送信者も受信者も制約しません。
受信者は未知のコードをデコードしなければなりません(MUST)。 AdCP バージョン X にピン留めされた受信者が、バージョン X+1 で導入された error.code(または標準語彙外のプラットフォーム固有コード)を運ぶレスポンスをデコードする場合:
- レスポンスを整形式として扱う — エンベロープを拒否したり、デシリアライズ例外を投げたり、汎用プロトコルエラーに格下げしたりしてはなりません(MUST NOT)。
- 存在する場合、
error.recovery(エラーエンベロープのトップレベルフィールド)から復旧分類を回復します。error.recoveryが規範的なキャリアであり、error-code.jsonのenumMetadata.recoveryは文書的なミラーです。 error.recoveryが不在の場合(レガシー送信者)、保守的なデフォルトを適用します。transientが未知のコードの安全なデフォルトです — リトライ・ウィズ・バックオフは terminal 分類より悪くなり得ず、マニフェストのerror_code_policy.default_unknown_recoveryがこれを正準のフォールバックとして文書化しています。transientデフォルトは § リトライロジックのリトライルールで制限されます — 受信者はmaxRetriesとジッター付き指数バックオフスケジュールを適用しなければならず(MUST)、transientデフォルトで無限にループしてはなりません(MUST NOT)。敵対的またはバグのある送信者は、コンフォーマントなクライアントに対して無制限のリトライを誘発できません。オープン enum のデコードルールは受信者をリトライバジェットから免除しません。
PROPOSAL_NOT_FOUND)を 3.0 ピン留めの受信者に発行する送信者はスペックに違反しません — 受信者は上記ルールによりそれを処理する必要があります。送信者が受信者のピン留めバージョンを知る場合(adcp_version エンベロープエコーまたはケイパビリティディスカバリー経由)、同等が存在するときはそのバージョンの語彙からコードを優先すべきです(SHOULD)。同等がないときは、より新しいまたはプラットフォーム固有のコードを発行してもよい(MAY)。
送信者はすべてのエラーで error.recovery を設定しなければなりません(MUST)。 これはバージョンスキューにわたる復旧セマンティクスの規範的なキャリアです — 受信者は知らないコードを確実に分類できませんが、常に error.recovery を読めます。省略は forward-compat ルールを無効にします。
なぜ重要か。 Forward-compatible デコードは、将来のメンテナンスライン(3.1.x、4.0.x、…)が古い受信者を壊さずに新しいエラーコードを追加的に出荷できるようにするワイヤーレベルの不変条件です。それがなければ、すべての新コードは次のマイナーまで保留されるワイヤー変更です — これが 3.0.x の現在の drift-lint ポリシーです。3.1+ でこれを備えると、新コードはメンテナンスラインの寿命中に登録でき、これはアダプターの実世界の拒否パスが新コードを表面化する際に重要です。
3.0.x ポリシーは変更なし。 3.0.x 受信者はこの規範ルールより前なので、3.0.x はサポート期間の残りの間ワイヤー安定を保ちます — 新コードは依然として次のマイナーで着地します。このルールは 3.1 以降の受信者契約を定めます。
Not-found の優先順位。 参照された識別子が解決しない場合、解決される型がリクエストから既知であれば、セラーはリソース固有のコードを返すべきです(SHOULD): product_id には PRODUCT_NOT_FOUND、package_id には PACKAGE_NOT_FOUND、media_buy_id には MEDIA_BUY_NOT_FOUND、creative_id には CREATIVE_NOT_FOUND、signal_id には SIGNAL_NOT_FOUND、SI session_id には SESSION_NOT_FOUND、account_id には ACCOUNT_NOT_FOUND、ガバナンス plan_id には PLAN_NOT_FOUND。専用コードのないリソース型(例: プロパティリスト、コンテンツ基準、権利付与、SI オファリング、プロポーザル、カタログ、イベントソース、コレクションリスト、ブランド、個別プロパティ)には REFERENCE_NOT_FOUND にフォールバックします。専用の標準コードを欠く型付きパラメーターは、カスタムの *_NOT_FOUND コードを作るのではなく REFERENCE_NOT_FOUND を使わなければなりません(MUST)— 語彙は上流のスペック変更で成長し、セラーごとのインフレでは成長しません。クライアントは最初に error.code で分岐すべきです(SHOULD)。リソース固有のコードにより、クライアントは error.field を解析せずにディスパッチできます。
多相パラメーター。 未解決の識別子が多相または型なしパラメーター(複数のリソース型を受け入れるフィールド)経由で供給された場合、解決される型にリソース固有のコードが存在しても、セラーは REFERENCE_NOT_FOUND を使わなければなりません(MUST)。多相パラメーターで型固有のコードを使うと、解決される型が未認可の呼び出し元に漏れます。多相性はツールスキーマのパラメーターの宣言された形状に対して — いかなるルックアップの前にも — 評価されるため、汎用の reference_id パラメーターは id が何に解決されるかに関わらず REFERENCE_NOT_FOUND にディスパッチします。ディスパッチ後に解決される型で評価すると漏洩が再導入されます。
ツールの宣言されたパラメーター形状は、あるツールバージョンについてすべての呼び出し元にわたって同一でなければならず(MUST)、ディスパッチルールは呼び出し元のアイデンティティに条件付けられてはなりません(MUST NOT)。テナント B には property_list_id を、テナント A には reference_id のみを公開するスキーマは、ケイパビリティディスカバリー自体を列挙オラクルに変えます(2 つのアイデンティティでスキーマを読み、diff を取る)。
アクセス不能な参照への統一レスポンス。 統一レスポンス要件はこの語彙のすべての not-found コード(REFERENCE_NOT_FOUND、SIGNAL_NOT_FOUND、CREATIVE_NOT_FOUND、MEDIA_BUY_NOT_FOUND、PACKAGE_NOT_FOUND、SESSION_NOT_FOUND、ACCOUNT_NOT_FOUND、PLAN_NOT_FOUND)に適用されます: セラーは「存在するが呼び出し元にアクセス権がない」に対して「存在しない」と同じレスポンスを返さなければなりません(MUST)。2 つを決して区別しないでください — これがクロステナント列挙が着地する方法です。
この MUST は error.code だけでなく、すべての観測可能なチャネルをカバーします:
- エラーオブジェクト。
error.code、error.message、error.field、error.detailsは 2 つのケース間でバイト等価でなければなりません(MUST)。REFERENCE_NOT_FOUNDにフォールバックする型付きパラメーターでは、error.fieldは true-miss と resolve-then-deny にわたって同一でなければならず(MUST)— 両方で省略されるか、両方で型中立な名前に置換されます。error.fieldは、パラメーター名が型中立な場合(例:reference_id)に入力パラメーターを名指してもよい(MAY)。元のパラメーター名が型を明かす場合(例:property_list_id)、error.fieldは省略されるか中立な名前に置換されなければなりません(MUST)。error.messageは汎用でなければなりません(MUST、"Property list not found"のようなリソース修飾テキストなし)。REFERENCE_NOT_FOUNDについては特に、セラーはerror.field、error.details、リソース修飾されたerror.messageを通じて解決される型を漏らしてはなりません(MUST NOT)。パラメーターが配列(例:catalog_ids、format_ids)の場合、error.fieldは配列パラメーター自体を名指さなければなりません(MUST)。セラーはerror.detailsで特定の解決不能な要素を列挙してもよい(MAY)— ただし要素が呼び出し元によってそのまま供給された場合のみです。セラーは要素レベルで「供給された要素は解決したが呼び出し元が未認可」を「供給された要素は存在しない」と区別してはなりません(MUST NOT)。それは配列エントリの粒度で列挙オラクルを再導入します。 - トランスポートステータス。 HTTP ステータスコード、A2A
task.status.state、MCPisErrorは 2 つのケース間で同一でなければなりません(MUST)。 - レスポンスヘッダー。
ETag、Cache-Control、リソース型ごとのレート制限バケット、CDN タグ、および値または存在がリソース型で異なる任意のヘッダーは同一でなければなりません(MUST)。 - 副作用。 Webhook ディスパッチと監査ログ書き込みは同一でなければなりません(MUST)— resolve-then-deny パスは、true-miss がしないような形で、テナント監査行を書いたり、バックグラウンド作業(検索インデクサー更新、キャッシュウォーマー、アクセスログアグリゲーター)をキューイングしたり、リソース型ごとのクォータ/レート制限カウンターをインクリメントしたり、任意のサブスクライバー(リソース所有者を含む)に Webhook を発火したりしてはなりません(MUST NOT)。resolve-then-deny パスがテナントごとの DB シャードまたはキャッシュに触れる場合、共同テナントの観測者がストレージ層メトリクスで区別できないよう、true-miss パスも同じ形状のストレージに触れなければなりません(MUST、例: 両方をテナント非依存のリゾルバー経由でルーティング)。
- 可観測性。 ダウンストリームのログ、APM スパン、サードパーティのエラーレポートテレメトリ(Sentry、Datadog、Rollbar など)は、呼び出し元にアクセス権がないとき解決されるリソース型でタグ付けされてはなりません(MUST NOT)。true-miss が発するトレースは resolve-then-deny が発するトレースと構造的に区別不能でなければなりません(MUST)。
SELECT ... WHERE id = ? AND tenant = ? のような単一クエリパターンは見た目は統一的ですが、行が別のテナントに存在するかどうかで実行計画、バッファプールのタッチ、認可者の呼び出しが異なります。id で解決し、次に読み込んだ行(true-miss では空の行)に対して認可する二段階パターンを、観測的統一性を自然に生む唯一のパターンとして優先してください。
キャッシュの温かさは別個のオラクルです: テナント B の id に対する温かいキャッシュは、最近誰かがそれにアクセスしたことを示します。セラーはキャッシュ投入を認可でゲートしてはなりません(MUST NOT)— true-miss の id は resolve-then-deny と同じ TTL でキャッシュ・アズ・ミスされなければならず(MUST)、または not-found レスポンスではキャッシュ読み取りがバイパスされなければなりません(MUST)。
自分で検証する。 ペア化プローブの adcp fuzz 不変条件は、ツールごとに 2 つのレスポンスを比較して統一レスポンス準拠を確認します。テナントセットアップ要件と CLI 呼び出しは Validate Your Agent — Preparing to test uniform error responsesを参照してください。フルストレングステストには 2 つの分離されたテナントが必要です。単一テナントの実行は「存在しない」レッグのみをカバーします。
認証とアクセス
請求とアカウントセットアップ
リクエストの請求またはアカウント形状の値がセラーに受け入れられない場合にsync_accounts が返します。2 つの請求拒否コードはどのゲートが発火したかを区別するため、エージェントはプロースを解析せずに正しい復旧(自律リトライ vs 人へのエスカレーション)にディスパッチできます。これらのコードが乗る二層アイデンティティモデルは バイヤーエージェントのアイデンティティを参照してください。
規範的要件:
- 確立されたエージェントアイデンティティなしの統一レスポンス。
BILLING_NOT_PERMITTED_FOR_AGENTは呼び出し元のセラーとのオンボード済み商業状態に基づいてBILLING_NOT_SUPPORTEDと異なります。アイデンティティ確立なしに per-agent コードを返すと、未認証プローブがコード選択を「このエージェントは agent-billable としてオンボードされているか?」のオラクルとして使えます —*_NOT_FOUND統一レスポンスルールと同じ形状です。境界線: セラーは、Agent identity に従う署名付きリクエスト導出、またはセラーのオンボーディングレコードのクレデンシャル・トゥ・エージェントマッピングを通じてエージェントアイデンティティが確立された場合にのみBILLING_NOT_PERMITTED_FOR_AGENTを発行しなければなりません(MUST)。それ以外のすべてのケース — 特定のエージェントレコードにマップされていない bearer 認証情報を含む — では、セラーはBILLING_NOT_SUPPORTEDを返さなければならず(MUST)、"account"スコープヒント自体がアカウント関係ごとのオラクルとして機能しないよう、このパスではerror.details.scopeを省略しなければなりません(MUST)。 BILLING_NOT_PERMITTED_FOR_AGENTの details 形状はクランプされる。error.detailsはerror-details/billing-not-permitted-for-agent.jsonに準拠しなければなりません(MUST):rejected_billing(エコー)と任意の単一のsuggested_billingリトライ値。スキーマはadditionalProperties: falseを設定します。形状はエージェントの完全な許可請求サブセット、レートカード、支払条件、信用限度、請求エンティティ、その他の per-agent 商業状態を運んではなりません(MUST NOT)— 単一プローブでの完全サブセット開示は、まさにクランプが防ぐオラクルです。- ワンショットリトライ。
error.details.suggested_billingでリトライして 2 つ目のBILLING_NOT_PERMITTED_FOR_AGENTを受け取ったバイヤーエージェントは、再度リトライするのではなく人間に表面化しなければなりません(MUST)。復旧はセラーが提案する単一のフォールバックに制限されます。さらなるイテレーションはセラーの設定ミスまたはエージェントが自律的に解決できないオンボーディング状態を示します。
@adcp/sdk/testing ラッパーではなく、セラーが直接返すレスポンス形状(sync_accounts レスポンスの accounts[].errors[] 配列 — task reference を参照)を使います。
BILLING_NOT_PERMITTED_FOR_AGENT、パススルー専用バイヤーエージェントが operator へのフォールバックを受け取る:
認可(RBAC)
呼び出し元が認証されているがリクエストの特定スコープを欠く場合に返されます。強制はセラーローカルです。発見可能性はsync_accounts と list_accounts のレスポンスのアカウントごとエントリの authorization オブジェクト経由です。完全な形状は Caller authorization を参照してください。
規範的要件:
FIELD_NOT_PERMITTEDはerror.fieldを設定しなければなりません(MUST) — 正確な問題フィールドパス(例:packages[0].budget、end_time)を。それがなければ、エージェントはフィールドを削除してリトライすることで確実に自動復旧できません。複数フィールドが許可されない場合、各error.fieldが明確になるようセラーは問題フィールドごとに 1 つのエラーを返すべきです(SHOULD)。単一エラーを返す場合、error.details.fieldsが完全なリストを含んでもよい(MAY)。SCOPE_INSUFFICIENTはerror.details.introspection_hintを含むべきです(SHOULD) — セラーが sync/list でauthorizationオブジェクトをサポートする場合。ストローマン形状:{ "task": "sync_accounts", "account": { ... } }、呼び出し元にスコープを再発見する場所を指し示します。これはコーディングエージェントの「エラーが出た。どうすればいい?」ループを閉じます。- 4 つのコードすべては
adcp_errorエンベロープフィールドとペイロードerrors[]配列の両方を設定しなければなりません(MUST) — 上記の二層モデルに従い。スコープエラーはスキーマエラーと同じ方法で型付きクライアントライブラリに引き継がれます。
SCOPE_INSUFFICIENT vs. PERMISSION_DENIED: タスク自体がこのアカウントのこの呼び出し元に付与されていない場合は SCOPE_INSUFFICIENT を使います。PERMISSION_DENIED は認証情報形状の失敗(署名付きコンテキストの欠落、署名検証の失敗)と、スコープが正しい抽象化ではない汎用のセラーポリシー拒否に予約します。
SCOPE_INSUFFICIENT vs. UNSUPPORTED_FEATURE: 両方が該当する場合 — セラーがタスクを実装しておらず、かつ呼び出し元がいずれにせよスコープされない — セラーは SCOPE_INSUFFICIENT を返すべきです(SHOULD)。UNSUPPORTED_FEATURE(呼び出し元はセラーを切り替えなければならない)より actionable(呼び出し元はより広いスコープを要求できる)だからです。実装するが呼び出し元に公開されていないタスクに UNSUPPORTED_FEATURE を返すセラーは、呼び出し元が対応できないケイパビリティ情報を漏らしています。
FIELD_NOT_PERMITTED vs. VALIDATION_ERROR: フィールドはタスクスキーマ上有効で、異なるスコープの呼び出し元からは受け入れられます。この呼び出し元の field_scopes に含まれないことを理由に拒否されます。
認可エラーの recovery: correctable について。 4 つの authz コードすべては correctable に分類され、リクエストを修正して再送できることを意味します。これはエージェントが自律的にリクエストを修正できることを意味しません — SCOPE_INSUFFICIENT と READ_ONLY_SCOPE の「修正」はオペレーターからの帯域外のスコープ付与であり、エージェントは自分で実行できません。エージェントは同じ認証情報に対して自動リトライするのではなく、authz エラーをオペレーターに表面化すべきです(SHOULD)。固定スコープに対するリトライループは防御姿勢ではなくバグです — 下記の制限された曖昧性解消リトライという狭い例外を除いて。FIELD_NOT_PERMITTED のみがエージェント自律の復旧パス(許可されないフィールドを削除して再送)を持ちます。
SCOPE_INSUFFICIENT と READ_ONLY_SCOPE のリトライ曖昧性解消。 セラーの 300 秒の認可リフレッシュウィンドウ内でのいずれかのコードの単一レスポンスは、クロスレプリカのちらつき — スペックがセラーに生成を禁じるが、バイヤーが実際には遭遇する一時的なインフラアーティファクト — と観測的に区別不能です。エラーをオペレーター介入が必要な確定的な correctable シグナルとして分類する前に、バイヤーはスコープが本当に不十分かを確立するため、制限されたリトライバジェット(3 回以下、各回 1〜5 秒のジッター付きバックオフで分離)を使い果たしてもよい(MAY)。これは曖昧性解消ロジックであり、correctable エラーからの復旧ではありません。制限されたリトライが成功なく尽きた後、バイヤーはエラーを表面化しなければならず(MUST)、自律的にリトライを続けてはなりません(MUST NOT)。このリトライ例外は FIELD_NOT_PERMITTED には適用されません — エージェント自律の strip-and-resubmit パスがそれに優先します。READ_ONLY_SCOPE の失効シナリオに関する注意を含む完全なガイダンスは Buyer response to SCOPE_INSUFFICIENT within the refresh windowを参照してください。
FIELD_NOT_PERMITTED — 両層を設定するエンベロープとペイロードの例:
field 値は削除するものを正確に特定します。details.permitted_fields(任意、助言的)は問題のタスクの許可リストを列挙し、エージェントがリトライ前に検証できます。SCOPE_INSUFFICIENT については、呼び出し元にスコープを再発見する場所を指すため details.introspection_hint: { "task": "list_accounts", "account": { ... } } を設定します。
エージェントごとの認可ゲート
per-buyer-agent ゲートは 3 つの異なる拒否パスにわたって発火し、それぞれ独自の判別子を持つため、呼び出し元はプロースを解析せずにディスパッチできます:
per-agent 商業ステータス拒否(
AGENT_SUSPENDED、AGENT_BLOCKED)は BILLING_NOT_PERMITTED_FOR_AGENT の先例に従います — コード自体が判別子で、レスポンスは明示的な error.details.scope フィールドを運びません。PERMISSION_DENIED + scope:"agent" パスは、reason が error-details/agent-permission-denied.json の閉じた enum に登録された非ステータスのプロビジョニングゲートに予約されます。"agent" 値は enums/error-scope.json の共有判別子語彙の登録済みサブセットです。
新コードを作るか reason enum を拡張するか。 ライフサイクル終端の per-agent 状態(suspended、blocked、将来の deny-list スタイルの状態)は専用コードを得ます — 異なる recovery 分類を運び、一級判別子に値します。非ステータスのプロビジョニングゲートと一時的拒否は、代わりに error-details/agent-permission-denied.json の reason enum(例: sandbox_only)を拡張します。スロットリング形状の拒否は新しい per-agent コードではなく RATE_LIMITED を再利用します。dedicated-code-per-state パターンは、ライフサイクル語彙が有限だから成立するのであり、すべての新ゲートのためのオープンレジストリとしてではありません。
3.0.5 プレースホルダーからの移行ノート。 3.0.5 は per-agent ライフサイクルのプレースホルダーとして details.status: ["suspended", "blocked"] 軸を持つ agent-permission-denied.json を出荷しました。3.1 はそのプレースホルダーを専用の AGENT_SUSPENDED / AGENT_BLOCKED コードに統合します — status 軸は agent-permission-denied.json から削除され、スキーマは scope:"agent" + reason:"sandbox_only" のみを受け入れます。3.0.5 プレースホルダーに対して統合したセラーは専用コードに移行しなければなりません(MUST)。プレースホルダー形状は保持されません。
規範的要件(3 つのコードすべてに一律適用):
- 確立されたエージェントアイデンティティなしの統一レスポンス。 per-agent 拒否は、Agent identity に従う署名付きリクエスト導出、またはセラーのオンボーディングレコードのクレデンシャル・トゥ・エージェントマッピングを通じてバイヤーエージェントアイデンティティが確立された場合にのみ意味があります。セラーはそのパスでのみ
AGENT_SUSPENDED/AGENT_BLOCKEDまたはdetails.scope: "agent"付きPERMISSION_DENIEDを発行しなければなりません(MUST)。それ以外のすべてのケース — 特定エージェントレコードにマップされていない bearer 認証情報を含む — では、汎用のPERMISSION_DENIEDを返し、error.details.scopeを省略しなければなりません(MUST)。アイデンティティ確立なしに per-agent コード(または per-agent スコープ)を返すと、未認証プローブがコード選択をクロステナントのオンボーディングオラクルとして使え、*_NOT_FOUND統一レスポンスルールとBILLING_NOT_PERMITTED_FOR_AGENTが閉じるのと同じ形状です。 - 未確立アイデンティティで省略ルールがカバーするチャネル。 MUST はすべての観測可能なチャネルに適用されます —
error.code/error.message/error.field/error.details(未確立アイデンティティパスで message は汎用でなければならない)、HTTP ステータス、A2Atask.status.state、MCPisError、レスポンスヘッダー(ETag、Cache-Control、per-agent レート制限バケット、CDN タグ)、副作用(監査ログ書き込み、Webhook ディスパッチ、バックグラウンドジョブのキューイング、per-agent クォータカウンター、DB シャードルーティング、オンボーディングレコードのキャッシュ投入)、可観測性(ログ、APM スパン、Sentry/Datadog/Rollbar タグは未確立アイデンティティパスでエージェントアイデンティティ、エージェントレコード参照、per-agent ゲート分類を運んではならない)。多相性はツールスキーマの宣言されたパラメーター形状に対していかなるオンボーディングレコードのルックアップの前にも評価されます — ツールの宣言された形状は、アイデンティティが確立されたかに関わらずすべての呼び出し元にわたって同一でなければなりません(MUST)。 - レイテンシー等価性。 セラーは両方のパスで同じ形状のオンボーディングレコードルックアップを実行しなければなりません(MUST、例: unmapped-credential パスで空のエージェントレコードに対して resolve-then-authorize)。ルックアップレイテンシーが「認証情報がエージェントにマップされていない」を「認証情報が suspended/blocked エージェントにマップされる」と区別しないように。resolve-then-authorize、decision-of-equivalent-shape、
*_NOT_FOUNDルールが要求するのと同じ姿勢。キャッシュ投入はアイデンティティ確立でゲートされてはなりません(MUST NOT)— さもなくばキャッシュ自体がヒット/ミスタイミングを通じてオラクルになります。 - リトライカウンターのサイドチャネル。 「自律リトライなし」ルール(下記)自体がサイドチャネルになってはなりません。セラーは、未確立アイデンティティパスで、true-unauthenticated パスと異なる per-agent リトライ/バックオフカウンターをインクリメントしたり、異なる
retry_afterを発行したり、異なるレート制限ヘッダーを表面化したりしてはなりません(MUST NOT)。no-retry ルールに準拠するバイヤーエージェントは、その準拠が別のテナントが読めるセラー側の per-agent 可観測性に反映されてはなりません(MUST NOT)。 - 3 つのパスのいずれにも per-agent 商業状態なし。
AGENT_SUSPENDED、AGENT_BLOCKED、PERMISSION_DENIED + scope:"agent"のいずれも、エージェントの完全な許可請求サブセット、レートカード、支払条件、信用限度、請求エンティティ、連絡チャネル、カスタム reason 文字列、その他の per-agent 商業状態を運びません — 単一プローブでの完全サブセット開示は、まさにクランプが防ぐオラクルです。AGENT_SUSPENDED/AGENT_BLOCKEDはerror.detailsペイロードを運びません。PERMISSION_DENIED + scope:"agent"はerror-details/agent-permission-denied.json(scope+ 登録済みreason、additionalProperties: false)に準拠しなければなりません(MUST)。新しいreason値は、クロス言語 SDK がプロース解析なしにディスパッチできるようスキーマの enum に追加されなければなりません(MUST)。将来の per-agent 状態面(エスカレーションチャネル、lift-policy URL)は、この形状を緩めるのではなく、独自のクランプされた details 形状を持つ新しい専用コードに属します。スキーマのadditionalProperties: falseはスキーマ検証ガードです。セラーはセラー側コンポーザーから受け取った認識されないまたは拡張キーを欠陥として扱い、送信前に削除しなければなりません(MUST)。 - per-agent ゲートで自律リトライなし。 3 つのコードすべては実際上 terminal です:
AGENT_SUSPENDEDとAGENT_BLOCKEDはenumMetadataのrecovery: "terminal"で直接宣言します。PERMISSION_DENIED + scope:"agent"パスはワイヤーレベルでcorrectable(登録済みPERMISSION_DENIED分類に一致)ですが実際上は terminal-pending-onboarding です — エージェントはサンドボックス専用プロビジョニングを一方的に解除できません。バイヤーエージェントは 3 つのいずれでも自動リトライするのではなくバイヤー側の人間に表面化しなければなりません(MUST)。再試行はゲートを強化するだけです。
error.code で分岐し、PERMISSION_DENIED の per-agent ゲートのみ details.scope にフォールスルーします:
AGENT_SUSPENDED:
PERMISSION_DENIED:
recovery: "correctable" は、error-code.json の enumMetadata に従う PERMISSION_DENIED の登録済み分類です — SDK は details.scope に基づいて登録値を切り替えてはなりません(MUST NOT)。バイヤーエージェントはワイヤーレベルの recovery フィールドに関わらず拒否を terminal-pending-onboarding として扱い、人間に表面化し、自動リトライしないようにしなければなりません(MUST)。(suspended/blocked パスでは、コード自体が recovery: "terminal" を直接運ぶため、この注意は適用されません。)
リクエストバリデーション
インベントリと商品
予算とクリエイティブ
システム
復旧分類
recovery フィールドを使ってエラーの処理方法を決定します:
未知の
recovery 値(前方互換性)は terminal として扱います。
リトライロジック
このセクションのルールは、呼び出し元がリトライしてよいすべてのtransient 分類エラーを制限します。これには § Forward-compatible decoding の下で未知のエラーコードに適用される transient デフォルトを含みます。未知のコードをデコードして transient にフォールバックする受信者は、下記の maxRetries とジッター付き指数バックオフスケジュールを適用しなければなりません(MUST)。オープン enum のデコードルールは受信者をリトライバジェットから免除しません。code=GO_FOREVER, recovery=transient を発行する敵対的またはバグのある送信者は、コンフォーマントなクライアントに対して無制限のリトライを誘発できません。
Normative throttling behavior
これらのルールは、呼び出し元がスロットリングカテゴリのエラー(RATE_LIMITED、または recovery が transient で details が rate-limited の detail 形状に準拠する任意のエラー)を受け取ったときに適用されます:
- 呼び出し元は存在する場合
retry_afterを尊重しなければならず(MUST)、示された秒数より早く同じリクエストをリトライしてはなりません(MUST NOT)。 - 呼び出し元は
retry_afterが不在の場合、ジッター付き指数バックオフを使うべきです(SHOULD)。ベース 2 秒、上限 60 秒、±25% ジッターが安全なデフォルトです。 - 呼び出し元は非スロットリングエラー(例:
INVALID_REQUEST、CREATIVE_REJECTED)をスロットリングされたかのように扱ってはなりません(MUST NOT)。他の理由で拒否されたレスポンスをバックオフのケイデンスでリトライするのは防御姿勢ではなくバグです。 - 呼び出し元は繰り返されるスロットリングを無限にリトライするのではなくオペレーターに表面化すべきです(SHOULD)。持続する
RATE_LIMITEDレスポンスは一時的な瞬きではなく、キャパシティまたはポリシーのシグナルです。 - セラーは、行儀の良い呼び出し元が意図的にバックオフできるよう、リクエストを黙ってキューイングまたはドロップするのではなく、
retry_afterを設定したRATE_LIMITEDを返すべきです(SHOULD)。 - セラーは、呼び出し元が 429 ごとに反応するのではなく先を計画できるよう、
rate-limitedの detail 形状(limit、remaining、window_seconds、scope)を設定してもよい(MAY)。
指数バックオフ
リトライ可能なエラーには指数バックオフを実装します:レート制限の処理
エラーハンドリングパターン
基本的なエラーハンドラー
ユーザーフレンドリーなメッセージ
技術的なエラーをユーザー向けメッセージに変換します:構造化されたエラーログ
デバッグのためにコンテキスト付きでエラーを記録します:Webhook のエラーハンドリング
Webhook 配信失敗
Webhook 配信に失敗した場合、ポーリングにフォールバックします:Webhook ハンドラーのエラー
Webhook エンドポイント内のエラーを丁寧に扱います:復旧戦略
コンテキストの復旧
コンテキストが期限切れの場合は新しい会話を開始します:部分的成功の扱い
一部のオペレーションは部分的に成功する場合があります:ガバナンスエラーパターン
check_governance はエラーオブジェクトではなく status フィールドを返します。ガバナンス結果はプロトコル的な意味でのエラーではありません — それらは判断です。AdCP タスクエラーとは別に扱ってください。
ガバナンスエージェントが内部的に人間のレビューを必要とする場合(例: アクションがエージェントの権限を超える)、
check_governance は任意の非同期タスクのように振る舞います — submitted/working ステータスを返し、最終的に approved または denied に解決します。これは特別なロジックではなく標準の非同期タスクライフサイクルで扱ってください。
プロトコル層からのガバナンスエラー(ガバナンス判断とは対照的に)は標準のエラー形式を使います。最も一般的なもの:
設定エラーパターン
CONFIGURATION_ERROR はセラー側のデプロイ欠陥を示します — mode: 'mock' で宣言されたが mock_upstream_url のないアカウント、upstream_url のない mode: 'live' または mode: 'sandbox' のプラットフォーム、セラープロセスで未設定の必須環境変数。バイヤーは修正できず、リトライは解決できず、セラー側のオペレーターが対処しなければなりません。カタログには設計上汎用の INTERNAL_ERROR コードがなく、CONFIGURATION_ERROR は意図的により狭い — 修復が「これをセラーのオペレーターに報告する」である actionable なスライスをカバーします。そのプロファイルに合わない不透明なクラッシュはカタログ非コード化のままです。セラーはプラットフォーム固有のコードを返してもよく(MAY)、バイヤーは前方互換ルールに従って recovery 分類にフォールバックします。
集約シグナル: リクエストごとに terminal、セラーごとに障害
単一のCONFIGURATION_ERROR はそれを受け取ったリクエストについて terminal です — バイヤーはセラー側の人間に表面化しなければならず(MUST)、自動リトライしてはなりません(MUST NOT)。短いウィンドウ内での同じセラーからの繰り返しの CONFIGURATION_ERROR は別種の運用シグナルです: セラー側の障害。バイヤー側のダッシュボードとアラートは、リクエストごと terminal の扱いとは別に、セラーごとの集約 CONFIGURATION_ERROR レートを障害インジケーターとして扱うべきです(SHOULD、例: 単一セラーから M 分に N 回発生でページング)。この収束が重要なのは、集約 CONFIGURATION_ERROR を汎用の terminal エラーとバケット化するバイヤーは、コードの存在意義であるセラー分離された障害シグナルを失うからです。
error.message: オペレーターが対処可能、デプロイ内部ではない
コード自体が判別子です —CONFIGURATION_ERROR は error.details 形状を運びません(AGENT_SUSPENDED / AGENT_BLOCKED の最小開示の先例が適用)。error.message が診断を運び、セラーはデプロイ内部をバイヤーに漏らさずにセラー側のオペレーターに有用なレベルに調整すべきです(SHOULD)。message はワイヤー可視です — 認証情報、接続文字列、完全なファイルパス、スタックトレースを含んではなりません(MUST NOT)。
有用(オペレーターは対処でき、バイヤーは悪用可能なことを何も学ばない):
ベストプラクティス
- まず
recoveryを確認する — エラーの処理方法として最も信頼できるシグナルです - リトライを実装する — 一時的エラーは指数バックオフを使用します
- レート制限を尊重する —
retry_afterの値を順守します - 未知のコードを適切に扱う —
recovery分類にフォールバックします - コンテキスト付きログ — デバッグ用に
code、recovery、fieldを含めます - フォールバックを用意する — 常に代替策を持ちます(例: Webhook 失敗時のポーリング)
- terminal エラーはリトライしない — 人のオペレーターにエスカレートします
- 部分成功に対応する — 成功レスポンスの警告も処理します
次のステップ
- Transport Bindings: エラーが MCP と A2A でどう伝達されるかは Transport Errors
- Task Lifecycle: ステータス処理は Task Lifecycle
- Webhooks: Webhook エラー処理は Webhooks
- Security: 認証エラーは Security