AdCP エラーはアプリケーション層のエラーです。トランスポートエラーチャネルではなく、ツール/タスクレスポンスに属します。このページでは error.json スキーマが MCP と A2A レスポンスエンベロープにどのようにマッピングされるかを定義します。
エラースキーマ自体、標準コード、リカバリー戦略についてはエラーハンドリングを参照。
層の分離
| 層 | 例 | チャネル |
|---|
| トランスポート | 接続拒否、不正な JSON-RPC、内部クラッシュ | JSON-RPC error / A2A プロトコルエラー |
| アプリケーション | RATE_LIMITED、BUDGET_TOO_LOW、CREATIVE_REJECTED | ツール/タスクレスポンスボディ |
トランスポートエラーはプロトコルライブラリが処理します。アプリケーションエラーはビジネスロジックが処理します。混在させると、AdCP エラーを有用にする構造化リカバリーデータが失われる。
MCP バインディング
ツールレベルエラー
すべての AdCP エラーコードの標準パス。ツールが実行し、リクエストを理解して、構造化エラーを返します。
現在の実用的なパス: ほとんどの MCP ホスト(Claude Desktop、Cursor、Windsurf)はエラーレスポンスの content テキストを読み取り、structuredContent を LLM やプログラム的なコンシューマーに公開しません。structuredContent の採用が広まるまで、テキストフォールバックパスがほとんどのエラーが抽出される方法です。サーバーは両方のパスをサポートすべきだ:
{
"content": [{"type": "text", "text": "{\"adcp_error\":{\"code\":\"RATE_LIMITED\",\"message\":\"Request rate exceeded\",\"retry_after\":5,\"recovery\":\"transient\"}}"}],
"isError": true,
"structuredContent": {
"adcp_error": {
"code": "RATE_LIMITED",
"message": "Request rate exceeded",
"retry_after": 5,
"recovery": "transient"
}
}
}
content テキストは AdCP エラーをテキストベース抽出用の JSON 文字列として含みます。structuredContent.adcp_error はプログラム的クライアントが同じエラーをサポートする場合に含みます。人間が読めるテキストを含めるサーバーは2番目のコンテンツアイテムとして追加すべきで、簡潔に保つ(1文):
{
"content": [
{"type": "text", "text": "{\"adcp_error\":{\"code\":\"RATE_LIMITED\",\"message\":\"Request rate exceeded\",\"retry_after\":5,\"recovery\":\"transient\"}}"},
{"type": "text", "text": "Rate limited — retry in 5s."}
],
"isError": true,
"structuredContent": {
"adcp_error": {
"code": "RATE_LIMITED",
"message": "Request rate exceeded",
"retry_after": 5,
"recovery": "transient"
}
}
}
structuredContent 存在時の簡潔なテキスト。 structuredContent が完全なエラーを含む場合、人間が読めるテキストコンテンツアイテムは1つの簡潔な文(例: “Rate limited — retry in 5s.”)にすべきです。エラーの詳細はすでに structuredContent と JSON テキストフォールバックにある。散文でエラー全体を繰り返すと、特にリトライ中に蓄積する一時的なエラーの場合、LLM コンテキストトークンが無駄になります。
adcp_error キー: 名前空間化により structuredContent にも現れる可能性のある成功データ(例: products)との衝突を避ける。単一のキーにより検出が簡単になります。
structuredContent には MCP 2025-03-26 以降が必要です。古い MCP バージョンのサーバーは structuredContent を省略する — content[0].text の JSON 文字列で十分です。クライアントはテキストフォールバックパスを通じてこれを解析する(クライアント検出順序を参照)。
トランスポートレベルエラー
ツールディスパッチの前にインフラ(API ゲートウェイ、レートリミットミドルウェア)がリクエストを拒否した場合、ツールは実行されない。data に AdCP エラーを含む予約済み JSON-RPC エラーコードを使用します:
{
"jsonrpc": "2.0",
"id": "req-123",
"error": {
"code": -32029,
"message": "Rate limit exceeded",
"data": {
"adcp_error": {
"code": "RATE_LIMITED",
"retry_after": 5,
"recovery": "transient"
}
}
}
}
予約済み JSON-RPC コード
| コード | AdCP エラーコード | タイミング |
|---|
-32029 | RATE_LIMITED | ツールディスパッチ前のインフラレートリミット |
-32028 | AUTH_REQUIRED | ツールディスパッチ前にミドルウェアが認証を拒否 |
-32027 | SERVICE_UNAVAILABLE | インフラヘルスチェック失敗、アップストリームダウン |
これらのコードは JSON-RPC サーバー定義範囲(-32000 から -32099)にある。他のすべての AdCP エラーコードはツールレベルパスのみを使用します。
MCP サーバー SDK の注意: ツールハンドラー内から McpError をスローすると JSON-RPC エラーレスポンスが生成される — SDK はそれを isError: true ツール結果に変換しない。つまり -32029 はミドルウェアからスローされても、ツールハンドラーからスローされても同じように機能します。しかしアプリケーション層エラー(ツールがリクエストを理解して構造化された失敗を返す場合)は、JSON-RPC エラーコードではなく上記の isError: true ツールレベルパスを使うべきです。-32029/-32028/-32027 はツールディスパッチ前にリクエストを拒否するインフラのために予約します。
MCP サーバー実装
function adcpErrorResponse(error) {
const adcpError = {
code: error.code,
message: error.message,
recovery: error.recovery,
...(error.retry_after != null && { retry_after: error.retry_after }),
...(error.field != null && { field: error.field }),
...(error.suggestion != null && { suggestion: error.suggestion }),
...(error.details != null && { details: error.details }),
};
return {
content: [{ type: "text", text: JSON.stringify({ adcp_error: adcpError }) }],
isError: true,
structuredContent: { adcp_error: adcpError },
};
}
server.tool(
"get_products",
"Search product catalog",
{ query: z.string() },
async ({ query }) => {
try {
const products = await searchProducts(query);
return {
content: [{ type: "text", text: `Found ${products.length} products` }],
structuredContent: { products },
};
} catch (err) {
if (err.code && err.recovery) {
return adcpErrorResponse(err);
}
throw err;
}
}
);
A2A バインディング
失敗したタスク
DataPart の AdCP エラーと人間/LLM 用の TextPart を含む status: "failed" を使用します:
{
"id": "task_456",
"status": {
"state": "failed",
"timestamp": "2025-01-22T10:30:00Z"
},
"artifacts": [{
"artifactId": "error-result",
"parts": [
{
"kind": "text",
"text": "Rate limit exceeded. Retry in 5 seconds."
},
{
"kind": "data",
"data": {
"adcp_error": {
"code": "RATE_LIMITED",
"message": "Request rate exceeded",
"retry_after": 5,
"recovery": "transient"
}
}
}
]
}]
}
これはA2A レスポンスフォーマットの規則に従う: 最終状態はデータに .artifacts を使用します。
「ラッパーなし」ルールとの関係。 adcp_error キーは失敗したタスクの意図的な例外です。成功レスポンスの DataPart がタスク固有のデータ(例: products)を含むのとは異なり、失敗したタスクの DataPart はエラーのみを含みます。このキーは型の識別子として機能し、クライアントがステータスだけに頼ることなくエラーと成功ペイロードを区別できるようにします。
エラー MIME タイプ(オプション)
A2A エージェントはエラー DataPart の metadata.mimeType を設定してもよい:
{
"kind": "data",
"data": { "adcp_error": { "code": "RATE_LIMITED", "recovery": "transient" } },
"metadata": { "mimeType": "application/vnd.adcp.error+json" }
}
クライアントは MIME タイプを必要としてはなりません。adcp_error キーが権威あるシグナルです。
クライアント検出順序
クライアントはこの順序で AdCP エラーを確認しなければなりません:
structuredContent.adcp_error(isError: true 付き)— MCP ツールレベルエラー
artifacts[].parts[].data.adcp_error — A2A タスクレベルエラー(アーティファクト)
status.message.parts[].data.adcp_error — A2A タスクレベルエラー(ステータスメッセージ)
error.data.adcp_error — JSON-RPC トランスポートレベルエラー
adcp_error キーを含む JSON パースされた content[].text — 古い MCP サーバー用のテキストフォールバック(isError レスポンスのみ)
- 構造化エラーが見つからない — 汎用エラー処理にフォールバック
クライアントは抽出されたエラーに string 型の code フィールドがあることを検証しなければなりません。検証が失敗した場合は、構造化エラーが見つからないとして扱います。
抽出 vs アクション。 上記の検出順序は抽出層だ — フィールド値をそのまま保持した生の adcp_error オブジェクトを返す(範囲外の retry_after を含む)。クランプ、リトライロジック、その他の動作要件はアクション層で適用されます(リカバリー動作を参照)。
実際には、実装はまずトランスポートタイプで分岐し、関連するパスのみを確認します:
function extractAdcpErrorFromMcp(response) {
if (!response.isError) return null;
// 1. structuredContent (preferred)
if (response.structuredContent?.adcp_error) {
return validate(response.structuredContent.adcp_error);
}
// 2. Text fallback
if (response.content) {
for (const item of response.content) {
if (item.type === 'text' && item.text) {
try {
const parsed = JSON.parse(item.text);
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)
&& parsed.adcp_error) {
return validate(parsed.adcp_error);
}
} catch { /* not JSON */ }
}
}
}
return null;
}
// Reject malformed or oversized payloads
function validate(error) {
if (!error || typeof error !== 'object' || Array.isArray(error)) return null;
if (typeof error.code !== 'string') return null;
if (error.code.length === 0 || error.code.length > 64) return null;
if (JSON.stringify(error).length > 4096) return null;
return error;
}
// For JSON-RPC errors (caught as McpError)
function extractAdcpErrorFromMcpError(error) {
return validate(error.data?.adcp_error);
}
リカバリー動作
抽出後、recovery フィールドに基づいてリカバリーを適用する:
| リカバリー | クライアント動作 |
|---|
transient | retry_after 秒後にリトライします。retry_after が欠如または非有限の場合は、クライアントの設定された初期遅延から始まる指数バックオフを使用します。 |
correctable | suggestion と field を呼び出し元に示し、自動リトライはしない |
terminal | 人間のオペレーターにエラーを示し、リトライしない |
retry_after の境界: セラーは 1 から 3600 秒の retry_after 値を返さなければなりません。クライアントはこの範囲外の値をクランプしなければなりません: 1 未満は 1 に、3600 超は 3600 になります。非有限値(NaN、Infinity)は欠如として扱わなければなりません。これにより、設定が間違ったサーバーからの積極的なリトライループと病的に長いストールの両方を防ぐ。
リトライ上限: バイヤーエージェントはオペレーションごとに最大リトライ回数(例: 3 回)と最大累積リトライ時間(例: 300 秒)を強制すべきです。リトライバジェットを超えて持続する一時的エラーはターミナルとしてエスカレーションすべきです。上限なしでは、すべてのリクエストで retry_after: 3600 を返す悪意のある、または設定が間違ったセラーがエージェントを無期限に停滞させる可能性があります。
recovery が欠如している場合: 標準エラーコードテーブルを使用してコードベースの分類にフォールバックします。これにより、レベル 1 サーバー(code と message のみを返す)でも、対応するクライアントから正しいリカバリー動作を得られます。コードも不明の場合は、terminal として扱います。
未知の recovery 値(前方互換性)については、terminal として扱います。
// Standard code → recovery mapping for when recovery field is absent
const CODE_RECOVERY = {
RATE_LIMITED: 'transient',
SERVICE_UNAVAILABLE: 'transient',
CONFLICT: 'transient',
INVALID_REQUEST: 'correctable',
AUTH_REQUIRED: 'correctable',
POLICY_VIOLATION: 'correctable',
PRODUCT_NOT_FOUND: 'correctable',
PRODUCT_UNAVAILABLE: 'correctable',
PROPOSAL_EXPIRED: 'correctable',
BUDGET_TOO_LOW: 'correctable',
CREATIVE_REJECTED: 'correctable',
UNSUPPORTED_FEATURE: 'correctable',
AUDIENCE_TOO_SMALL: 'correctable',
ACCOUNT_SETUP_REQUIRED: 'correctable',
ACCOUNT_AMBIGUOUS: 'correctable',
COMPLIANCE_UNSATISFIED: 'correctable',
ACCOUNT_NOT_FOUND: 'terminal',
ACCOUNT_PAYMENT_REQUIRED: 'terminal',
ACCOUNT_SUSPENDED: 'terminal',
BUDGET_EXHAUSTED: 'terminal',
};
function getRecovery(adcpError) {
if (adcpError.recovery) return adcpError.recovery;
return CODE_RECOVERY[adcpError.code] || 'terminal';
}
function handleAdcpError(adcpError) {
switch (getRecovery(adcpError)) {
case 'transient':
const raw = adcpError.retry_after;
const delay = Number.isFinite(raw) ? Math.max(1, Math.min(3600, raw)) : null;
return { action: 'retry', delaySeconds: delay };
case 'correctable':
return {
action: 'fix_request',
field: adcpError.field,
suggestion: adcpError.suggestion,
};
case 'terminal':
return { action: 'escalate', message: adcpError.message };
default:
// Unknown recovery value: treat as terminal
return { action: 'escalate', message: adcpError.message };
}
}
推奨 details 形式
details フィールドはオープンオブジェクトです。相互運用性の発散を防ぐため、セラーは一般的なエラーコードの details を設定する際にこれらの標準キーを使用すべきだ:
RATE_LIMITED
{
"code": "RATE_LIMITED",
"retry_after": 5,
"recovery": "transient",
"details": {
"limit": 100,
"remaining": 0,
"window_seconds": 60,
"scope": "account"
}
}
| キー | 型 | 説明 |
|---|
limit | number | ウィンドウ内で許可される最大リクエスト数 |
remaining | number | 現在のウィンドウで残っているリクエスト数 |
window_seconds | number | レートリミットウィンドウの長さ |
scope | string | 制限が適用される対象: account、tool、または global |
BUDGET_TOO_LOW
{
"code": "BUDGET_TOO_LOW",
"recovery": "correctable",
"details": {
"minimum_budget": 500,
"currency": "USD"
}
}
| キー | 型 | 説明 |
|---|
minimum_budget | number | このプロダクトのセラー最小予算 |
currency | string | ISO 4217 通貨コード |
AUDIENCE_TOO_SMALL
{
"code": "AUDIENCE_TOO_SMALL",
"recovery": "correctable",
"details": {
"minimum_size": 10000,
"current_size": 2500
}
}
| キー | 型 | 説明 |
|---|
minimum_size | number | 必要な最小オーディエンスサイズ |
current_size | number | 現在のオーディエンスサイズ |
ACCOUNT_SETUP_REQUIRED
{
"code": "ACCOUNT_SETUP_REQUIRED",
"recovery": "correctable",
"details": {
"setup_url": "https://seller.example.com/setup/acct_123",
"setup_steps": ["Accept terms of service", "Add payment method"]
}
}
| キー | 型 | 説明 |
|---|
setup_url | string | アカウントセットアップを完了できる URL |
setup_steps | string[] | アカウントが準備できるまでの残りのステップ |
CREATIVE_REJECTED
{
"code": "CREATIVE_REJECTED",
"recovery": "correctable",
"suggestion": "Revise creative to comply with alcohol advertising policy",
"details": {
"policy_id": "alcohol-advertising-v2",
"policy_url": "https://seller.example.com/policies/alcohol-advertising",
"reasons": ["Contains health claims not permitted for alcohol products"]
}
}
| キー | 型 | 説明 |
|---|
policy_id | string | 違反したポリシーの識別子 |
policy_url | string | 完全なポリシーを確認できる URL |
reasons | string[] | クリエイティブが拒否された具体的な理由 |
POLICY_VIOLATION
{
"code": "POLICY_VIOLATION",
"recovery": "correctable",
"details": {
"policy_id": "targeting-restrictions-v3",
"policy_url": "https://seller.example.com/policies/targeting",
"violated_rules": ["No age-based targeting for financial products"]
}
}
| キー | 型 | 説明 |
|---|
policy_id | string | 違反したポリシーの識別子 |
policy_url | string | 完全なポリシーを確認できる URL |
violated_rules | string[] | 違反した具体的なルール |
CONFLICT
{
"code": "CONFLICT",
"recovery": "transient",
"message": "Resource was modified since last read",
"details": {
"resource_id": "mb_12345",
"expected_version": 3,
"current_version": 5
}
}
| キー | 型 | 説明 |
|---|
resource_id | string | 競合するリソースの識別子 |
expected_version | number | string | クライアントが操作していたバージョンまたは ETag |
current_version | number | string | サーバー上の現在のバージョンまたは ETag |
サイズガイダンス
セラーは details をコンパクトに保つべきです。エラーレスポンスは LLM コンテキストウィンドウを通じて流れ、すべてのトークンにコストがかかる — リトライをトリガーする一時的なエラーは1つの会話内で複数のエラーレスポンスを蓄積することがあります。ガイドラインとして、details を 500 シリアル化 JSON バイト未満に保つ(UTF-8 で JSON.stringify(details).length を使用 — 非 ASCII コンテンツには重要だ)。
details スキーマ
推奨される details 形式のすべての JSON スキーマはエラーコード列挙と一緒に公開されています:
これらのスキーマは推奨であり、必須ではありません。details を完全に省略するセラーも適合しています。エージェントは特定の details キーを要求してはなりません — details が欠如または予期しない形状の場合は code、message、recovery にフォールバックします。
セラー固有のエラーコード
セラーは標準語彙にないエラーコードを使用してもよい。セラー固有のコードを標準コードと区別し、セラー間の衝突を避けるために:
- セラー固有のコードは
X_{VENDOR}_{CODE} 形式を使用しなければなりません(例: X_STREAMHAUS_FLOOR_NOT_MET)
{VENDOR} はベンダーエラーコードレジストリに登録された大文字英数字識別子でなければなりません(/^[A-Z][A-Z0-9]{1,19}$/ にマッチ)
{CODE} は大文字英数字とアンダースコアでなければなりません(/^[A-Z][A-Z0-9_]{1,39}$/ にマッチ)
- エージェントは不明なコードを
recovery 分類にフォールバックして処理しなければなりません
- 不明なコードで
recovery が欠如している場合は terminal として扱います
- セラーは PR を提出することでベンダーエラーコードレジストリにベンダープレフィックスとコードを登録すべきです
function handleError(error) {
if (isStandardErrorCode(error.code)) {
// Handle per standard code semantics
return handleStandardError(error);
}
// Unknown/vendor code: fall back to recovery classification
return handleByRecovery(error);
}
クライアントライブラリ要件
この仕様を実装するクライアントライブラリ(@adcp/client など)は以下を満たさなければなりません:
-
構造化エラーを自動的に抽出します。 コンシューマーは、メッセージ文字列の汎用エラーではなく、
code、recovery、retryAfter、field、suggestion、details を持つ型付きエラーオブジェクトを受け取るべきです。
-
検出順序を実装します。 すべてのパスを順番に確認します:
structuredContent、アーティファクト、status.message.parts、error.data、テキストフォールバック。
-
抽出されたエラーを検証します。
code が空でない文字列(最大 64 文字)であり、シリアル化されたペイロードの合計が 4096 バイトを超えないことを確認します。検証に失敗したペイロードは破棄します。
-
テキストフォールバックを
isError でガードします。 isError が true の MCP レスポンスでのみ JSON ベースのテキスト抽出を試みる。JSON コンテンツを含む成功レスポンスをエラーとして解釈してはなりません。
-
リカバリーメタデータを保持します。 抽出されたエラーには
recovery と retry_after を含め、呼び出し元が再解析なしにリトライロジックを実装できるようにします。
-
未知のリカバリー値を処理します。 未知の
recovery 値は terminal として扱います。
-
retry_after をクランプします。 1 未満は 1 に、3600 超は 3600 になります。非有限値(NaN、Infinity)は欠如として扱わなければなりません。
-
テキストフォールバックをサポートします。
structuredContent なしの MCP isError レスポンスの content[].text に対して JSON.parse を試みる。structuredContent の採用が広まるまで、これが主要な抽出パスになります。
クライアントライブラリは追加で:
retry_after が存在する場合に指数バックオフで transient エラーを自動リトライしてもよい
- コンシューマーがリトライ動作を設定するための
retryPolicy オプションを公開してもよい
STANDARD_ERROR_CODES テーブルを使用して標準エラーコードを型付きエラーサブクラスにマッピングしてもよい
テストベクター
機械可読のテストベクターは /static/test-vectors/transport-error-mapping.json で入手可能です。各ベクターには以下が含まれます:
transport: mcp または a2a
path: 抽出パス(structuredContent、jsonrpc_error、text_fallback、artifact)
response: トランスポート固有のレスポンスエンベロープ
expected_error: 抽出されるべき AdCP エラー(またはレガシーサーバーの場合は null)
expected_action: retry、surface_to_caller、escalate_to_human、または generic_error
クライアントライブラリはこれらのベクターに対して抽出ロジックを検証すべきです。
エージェントチェーンでのエラー変換
セラーエージェントがアップストリームサービス(API、データベース、他のエージェント)を呼び出す場合、アップストリームの失敗は呼び出し元に返す前に変換しなければなりません。
ルール 1: アップストリームエラーを AdCP エラーコードに変換します。 生のアップストリームエラーをそのまま渡してはなりません。セラーの内部 API からの HTTP 429 は RATE_LIMITED になります。データベース接続タイムアウトは SERVICE_UNAVAILABLE になります。バイヤーは関係のないシステムのエラーフォーマットを見るべきではありません。
ルール 2: 呼び出し元の視点からリカバリーを分類します。 セラーがバイヤーのアクションなしにアップストリームの問題を修正できる場合、エラーは transient または terminal だ — correctable ではありません。correctable エラーはバイヤーが何かを変更する必要があることを意味します。例えば: セラーのアップストリームクリエイティブレビュー API が広告を拒否した場合、それは correctable(バイヤーはクリエイティブを修正できます)。しかしセラーの内部課金システムがダウンしている場合、アップストリームエラーが 500 であっても、それは transient(バイヤーはリトライすべき)だ。
ルール 3: 中間者は保持するか変換するが、決して削除しません。 バイヤーとセラーの間に座る中間者(例: 複数のセラーにルーティングするエージェンシーエージェント)は以下を行わなければなりません:
- アップストリームがすでに AdCP 準拠の場合は AdCP エラーを変換せずに渡す、または
- アップストリームが異なるフォーマットを使用している場合はエラーを有効な AdCP エラーに変換する
中間者は渡すエラーから recovery、retry_after、または details を削除してはなりません。中間者は複数のアップストリームセラーからのエラーを errors 配列に集約してもよく、各エラーは元の code と recovery を保持します。
// Seller-side: translate upstream errors for the buyer
function translateUpstreamError(upstreamError) {
if (upstreamError.status === 429) {
return {
code: 'RATE_LIMITED',
message: 'Request rate exceeded',
recovery: 'transient',
retry_after: upstreamError.headers?.['retry-after'] || 10,
};
}
if (upstreamError.status >= 500) {
return {
code: 'SERVICE_UNAVAILABLE',
message: 'Service temporarily unavailable',
recovery: 'transient',
};
}
// Never expose upstream details to the buyer
return {
code: 'SERVICE_UNAVAILABLE',
message: 'An internal error occurred',
recovery: 'transient',
};
}
セキュリティ上の考慮事項
エラーレスポンスは LLM コンテキストを通じて流れる。すべてのフィールドはクライアント向けです。
セラーの要件
実装は以下を含めてはなりません:
- 内部サービス名、ホスト名、または IP アドレス
- データベースエラーテキスト、SQL フラグメント、またはクエリプラン
- スタックトレースまたはファイルパス
- 内部サービスからのアップストリーム API レスポンス
- 認証情報、トークン、またはセッション識別子
suggestion の境界: 特定の閾値、有効な識別子、リソースの存在を明かすのではなく、一般的な修正ガイダンス(例: “Increase budget to meet minimum”)を提供します。
retry_after の一貫性: タイミングサイドチャネルを避けるため、ターゲットリソースのプロパティではなく、呼び出し元のレートリミット状態を反映した一貫した値を返します。
トランスポートレベルコードの粒度: 予約済み JSON-RPC コード(-32029、-32028、-32027)はインフラエラーの分類を可能にします。エンドポイントのフィンガープリントを最小化したい実装は、これらを単一のコードに統合してもよい。
バイヤーエージェントの要件
エラーフィールドを通じたプロンプトインジェクション。 message、suggestion、field、details、およびそれらの中のすべての文字列値は、バイヤーエージェントの LLM コンテキストに入るセラー制御コンテンツです。悪意のある、または侵害されたセラーは、バイヤーエージェントを操作することを目的とした指示を含む値を作成できます。
バイヤーエージェントは以下を行わなければなりません:
- すべてのリカバリー決定を
code と recovery のみを通じてルーティングします。 アクション可能な指示のために message、suggestion、または details 値を解析してはなりません。上記の handleAdcpError 関数はこのパターンを示している — メッセージコンテンツではなく recovery で切り替える。
- セラー提供の文字列にデータ境界を使用します。 エラーフィールド値を LLM コンテキストに含める場合、システムプロンプトが信頼できないセラーデータとして指定する明示的なデータデリミター(例: 構造化されたツールレスポンスフィールド、XML スタイルタグ)の内側に置く。セラー提供の文字列を散文や指示に補間しません。
- セラー文字列を LLM コンテキストに含める前に長さ制限を適用する:
message(256 バイト)、suggestion(512 バイト)。サイレントにトランケートします。
- すべての文字列フィールドから非印刷可能文字を除去する: 制御文字(U+0000–U+001F)、ゼロ幅文字(U+200B–U+200F)、双方向オーバーライド文字(U+202A–U+202E)。
- 最大ペイロードサイズを強制します。 クライアントは
JSON.stringify(error).length が 4096 バイトを超える抽出された adcp_error オブジェクトを破棄しなければなりません。これにより過大な details オブジェクトによるコンテキストウィンドウの消耗を防ぐ。
- オブジェクトミューテーション操作でダイナミックプロパティパスとして
field を使用しない(例: lodash.set、ブラケット表記チェーン)。field 値は表示とフィールドレベルの UI ハイライトのみ用です。
- キーをフィルタリングせずに
Object.assign、スプレッド演算子、またはシャローコピーを通じて抽出されたエラーオブジェクトをアプリケーション状態にマージしません。 __proto__ や constructor などのセラー制御キーは一部のランタイムでプロトタイプ汚染を引き起こす可能性があります。
- 生の
details オブジェクトをシステムプロンプトやツールの説明に含めない。
URL 検証。 details.setup_url(ACCOUNT_SETUP_REQUIRED エラー内)は、アカウントセットアップを完了するためにユーザーまたはエージェントが辿ることができるセラー提供の URL だ。クライアントは setup_url が https スキームを使用し、ユーザー情報コンポーネントを含まず(例: https://user:pass@evil.com)、ドメインがセラーの既知のドメインと一致することを検証しなければなりません。これらの確認に失敗した URL は拒否しなければなりません。
details.policy_url(CREATIVE_REJECTED および POLICY_VIOLATION エラー内)は情報提供のみです。クライアントは同じ検証を適用すべきです。すべてのセラー提供 URL は https 以外のスキーム(http、javascript、data、file)を使用している場合は拒否しなければなりません。
関連情報