Documentation Index
Fetch the complete documentation index at: https://adcp-docs-ja.pier1.co.jp/llms.txt
Use this file to discover all available pages before exploring further.
AdCP は全オペレーションで一貫したエラーハンドリングを行います。エラー分類を理解し、適切な復旧戦略を実装することが堅牢な統合には不可欠です。
準拠レベル
セラーはエラーハンドリングを段階的に採用できます。各レベルは前のレベルの上に構築されます:
| Level | 実装する内容 | エージェントができること |
|---|
| Level 1 | すべてのエラーに code と message を返す | エラーコードで失敗を分類できる |
| Level 2 | recovery、retry_after、field、suggestion を追加する | 一時的エラーの自動リトライと修正可能なエラーの自己訂正ができる |
| Level 3 | トランスポートバインディング で MCP の structuredContent または A2A のアーティファクト DataPart にエラーを入れる | プログラム的クライアントがテキスト解析なしに型付きエラーオブジェクトを取得できる |
Level 1 は準拠実装の最低要件です。Level 2 でエージェント主導の復旧が可能になります — recovery がなければエージェントはエラーコードから推測するしかありません。Level 3 で @adcp/client のようなクライアントライブラリが完全な型付きエラーオブジェクトを提供できます。
エラーの分類
1. プロトコルエラー
AdCP ビジネスロジック外の通信・接続問題:
- ネットワークタイムアウト
- 接続拒否
- TLS/SSL エラー
- JSON パースエラー
対応: 指数バックオフでリトライします。
2. タスクエラー
status: "failed" で返るビジネスロジックの失敗:
- 在庫不足
- 無効なターゲティング
- 予算バリデーション失敗
- リソース未検出
対応: recovery フィールドを確認して、リトライするか、リクエストを修正するか、エスカレートするかを判断します。
3. バリデーションエラー
スキーマ検証に失敗する不正リクエスト:
対応: リクエスト形式を修正して再送します(多くは開発時の問題です)。
エラーレスポンス形式
失敗した処理はステータス failed とエラー詳細を返します。エラーオブジェクトは error.json スキーマに従います:
{
"status": "failed",
"message": "Budget is below the seller's minimum for this product",
"errors": [
{
"code": "BUDGET_TOO_LOW",
"message": "Budget is below the seller's minimum for this product",
"recovery": "correctable",
"field": "budget.total",
"suggestion": "Increase budget to at least 500 USD",
"details": {
"minimum_budget": 500,
"currency": "USD"
}
}
]
}
エラーオブジェクトのフィールド
これらのフィールドは error.json スキーマで定義されています:
| Field | Type | Required | Description |
|---|
code | string | Yes | 標準コードまたはセラー固有の機械判読用エラーコード |
message | string | Yes | 人向けのエラー説明 |
recovery | string | No | エージェントの復旧分類: transient、correctable、terminal |
retry_after | number | No | リトライまでの待機秒数(一時的エラー) |
field | string | No | エラーの原因フィールドパス(例: packages[0].targeting) |
suggestion | string | No | エラーの修正提案 |
details | object | No | 追加のコンテキスト情報 |
標準エラーコード
AdCP は error-code.json に 20 の標準エラーコードを定義しています。セラーはプラットフォーム固有エラーにこの語彙外のコードを使用してもよいです。エージェントは未知のコードを recovery 分類にフォールバックして処理しなければなりません。
認証とアクセス
| Code | Recovery | Description | Resolution |
|---|
AUTH_REQUIRED | correctable | 認証が必要、または認証情報が無効 | auth ヘッダーで認証情報を提供する |
ACCOUNT_NOT_FOUND | terminal | アカウント参照を解決できない | list_accounts で確認するか、セラーに連絡する |
ACCOUNT_SETUP_REQUIRED | correctable | 使用前にアカウントのセットアップが必要 | details.setup の URL または手順を確認する |
ACCOUNT_AMBIGUOUS | correctable | 自然キーが複数のアカウントに解決する | 明示的な account_id またはより具体的な自然キーを渡す |
ACCOUNT_PAYMENT_REQUIRED | terminal | 未払い残高の支払いが必要 | バイヤーが請求を解決しなければなりません |
ACCOUNT_SUSPENDED | terminal | アカウントが停止されている | セラーに連絡して解決する |
リクエストバリデーション
| Code | Recovery | Description | Resolution |
|---|
INVALID_REQUEST | correctable | リクエストが不正またはスキーマ制約に違反している | リクエストパラメーターを確認して修正する |
UNSUPPORTED_FEATURE | correctable | このセラーがサポートしていない機能を要求している | get_adcp_capabilities を確認してサポートされていないフィールドを削除する |
POLICY_VIOLATION | correctable | リクエストがコンテンツまたは広告ポリシーに違反している | エラー詳細のポリシー要件を確認する |
COMPLIANCE_UNSATISFIED | correctable | 必要な開示事項をターゲットフォーマットで満たせない | 必要な開示機能をサポートするフォーマットを選択する |
インベントリと商品
| Code | Recovery | Description | Resolution |
|---|
PRODUCT_NOT_FOUND | correctable | 参照した商品 ID が不明または期限切れ | 無効な ID を削除するか、get_products で再探索する |
PRODUCT_UNAVAILABLE | correctable | 商品が売り切れまたは利用不可 | 別の商品を選択する |
PROPOSAL_EXPIRED | correctable | 参照したプロポーザルの expires_at が過ぎている | get_products を実行して新しいプロポーザルを取得する |
AUDIENCE_TOO_SMALL | correctable | オーディエンスセグメントが最小サイズを下回っている | ターゲティングを広げるか、より多くのオーディエンスメンバーをアップロードする |
予算とクリエイティブ
| Code | Recovery | Description | Resolution |
|---|
BUDGET_TOO_LOW | correctable | 予算がセラーの最小値を下回っている | 予算を増やすか capabilities.media_buy.limits を確認する |
BUDGET_EXHAUSTED | terminal | アカウントまたはキャンペーン予算を使い切った | バイヤーが資金を追加するか予算上限を増やさなければなりません |
CREATIVE_REJECTED | correctable | クリエイティブがコンテンツポリシーレビューに不合格 | セラーの advertising_policies に従って修正する |
システム
| Code | Recovery | Description | Resolution |
|---|
RATE_LIMITED | transient | リクエストレートを超過した | retry_after 秒待ってからリトライする |
SERVICE_UNAVAILABLE | transient | セラーサービスが一時的に利用不可 | 指数バックオフでリトライする |
CONFLICT | transient | 同時変更が検出された | リソースを再読み込みして最新状態でリトライする |
復旧分類
recovery フィールドを使ってエラーの処理方法を決定します:
| Recovery | 意味 | アクション |
|---|
transient | 一時的な失敗(レート制限、サービス停止、競合) | retry_after 後または指数バックオフでリトライする |
correctable | リクエストを修正して再送可能(無効フィールド、予算不足、クリエイティブ不合格) | リクエストを変更してリトライする |
terminal | 人の対応が必要(アカウント停止、支払い必要) | 人のオペレーターにエスカレートする |
未知の recovery 値(前方互換性)は terminal として扱います。
function isRetryable(error) {
// Use recovery field when available
if (error.recovery) {
return error.recovery === 'transient';
}
// Network errors are retryable
if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT') {
return true;
}
// Fall back to error code matching
return ['RATE_LIMITED', 'SERVICE_UNAVAILABLE', 'CONFLICT'].includes(error.code);
}
リトライロジック
指数バックオフ
リトライ可能なエラーには指数バックオフを実装します:
async function retryWithBackoff(fn, options = {}) {
const {
maxRetries = 3,
baseDelay = 1000,
maxDelay = 60000
} = options;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (!isRetryable(error) || attempt === maxRetries) {
throw error;
}
// Use retry_after when available, otherwise exponential backoff
const retryAfter = error.retry_after ||
Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
// Add jitter to prevent thundering herd
const jitter = retryAfter * (0.75 + Math.random() * 0.5);
await sleep(jitter);
}
}
}
レート制限の処理
async function handleRateLimit(error, retryFn) {
if (error.recovery !== 'transient' &&
error.code !== 'RATE_LIMITED') {
throw error;
}
const retryAfter = error.retry_after || 60;
console.log(`Rate limited. Waiting ${retryAfter} seconds...`);
await sleep(retryAfter * 1000);
return retryFn();
}
エラーハンドリングパターン
基本的なエラーハンドラー
async function handleAdcpError(error) {
// Use recovery classification when available
switch (error.recovery) {
case 'transient':
const delay = error.retry_after
? error.retry_after * 1000
: 5000;
await sleep(delay);
return retry();
case 'correctable':
// Surface suggestion so the request can be fixed
if (error.suggestion) {
console.log('Suggestion:', error.suggestion);
}
if (error.field) {
console.log('Problem field:', error.field);
}
throw error;
case 'terminal':
console.error('Terminal error:', error.message);
throw error;
}
// Fall back to error code matching
switch (error.code) {
case 'AUTH_REQUIRED':
await refreshCredentials();
return retry();
case 'INVALID_REQUEST':
console.error('Validation error:', error);
throw error;
default:
console.error('AdCP error:', error);
throw error;
}
}
ユーザーフレンドリーなメッセージ
技術的なエラーをユーザー向けメッセージに変換します:
const USER_MESSAGES = {
'RATE_LIMITED': 'Too many requests. Please wait a moment and try again.',
'BUDGET_TOO_LOW': 'This is below the seller\'s minimum budget. Increase your budget.',
'PRODUCT_NOT_FOUND': 'One or more products could not be found. Try searching again.',
'ACCOUNT_SUSPENDED': 'Your account has been suspended. Contact the seller to resolve.',
'SERVICE_UNAVAILABLE': 'The service is temporarily unavailable. Please try again in a few minutes.',
'CREATIVE_REJECTED': 'Your creative did not pass policy review. Check the suggestion for details.',
'AUDIENCE_TOO_SMALL': 'Your target audience is too small. Try broadening your targeting.'
};
function getUserMessage(code, fallbackMessage) {
return USER_MESSAGES[code] || fallbackMessage || 'An unexpected error occurred. Please try again.';
}
構造化されたエラーログ
デバッグのためにコンテキスト付きでエラーを記録します:
function logError(error, context = {}) {
console.error('AdCP Error:', {
code: error.code,
recovery: error.recovery,
message: error.message,
field: error.field,
timestamp: new Date().toISOString(),
...context,
// Don't log sensitive data
// NO: credentials, briefs, PII
});
}
Webhook のエラーハンドリング
Webhook 配信失敗
Webhook 配信に失敗した場合、ポーリングにフォールバックします:
class WebhookErrorHandler {
async onDeliveryFailure(taskId, error) {
console.warn(`Webhook delivery failed for ${taskId}:`, error);
// Start polling as fallback
this.startPolling(taskId);
// Track failure for monitoring
this.metrics.incrementCounter('webhook_failures');
}
async startPolling(taskId) {
const response = await adcp.call('tasks/get', {
task_id: taskId,
include_result: true
});
if (['completed', 'failed', 'canceled'].includes(response.status)) {
await this.processResult(taskId, response);
} else {
// Schedule next poll
setTimeout(() => this.startPolling(taskId), 30000);
}
}
}
Webhook ハンドラーのエラー
Webhook エンドポイント内のエラーを丁寧に扱います:
app.post('/webhooks/adcp', async (req, res) => {
try {
// Always respond quickly
res.status(200).json({ status: 'received' });
// Process asynchronously
await processWebhookAsync(req.body);
} catch (error) {
// Log error but don't fail the response
console.error('Webhook processing error:', error);
// Move to dead letter queue for investigation
await deadLetterQueue.add(req.body, error);
}
});
復旧戦略
コンテキストの復旧
コンテキストが期限切れの場合は新しい会話を開始します:
async function callWithContextRecovery(request) {
try {
return await adcp.call(request);
} catch (error) {
if (error.code === 'INVALID_REQUEST' &&
error.message?.includes('context not found')) {
// Clear stale context and retry
delete request.context_id;
return await adcp.call(request);
}
throw error;
}
}
部分的成功の扱い
一部のオペレーションは部分的に成功する場合があります:
{
"status": "completed",
"message": "Created media buy with warnings",
"media_buy_id": "mb_123",
"errors": [
{
"code": "COMPLIANCE_UNSATISFIED",
"message": "Required disclosure position not supported by one placement",
"field": "packages[0].placements[2]",
"suggestion": "Choose a format that supports the required disclosure positions"
}
]
}
部分成功を処理します:
function handlePartialSuccess(response) {
if (response.status === 'completed' && response.errors?.length) {
// Show warnings to user
for (const warning of response.errors) {
showWarning(warning.message, warning.suggestion);
}
}
// Continue with successful result
return response;
}
ベストプラクティス
- まず
recovery を確認する — エラーの処理方法として最も信頼できるシグナルです
- リトライを実装する — 一時的エラーは指数バックオフを使用します
- レート制限を尊重する —
retry_after の値を順守します
- 未知のコードを適切に扱う —
recovery 分類にフォールバックします
- コンテキスト付きログ — デバッグ用に
code、recovery、field を含めます
- フォールバックを用意する — 常に代替策を持ちます(例: Webhook 失敗時のポーリング)
- terminal エラーはリトライしない — 人のオペレーターにエスカレートします
- 部分成功に対応する — 成功レスポンスの警告も処理します
次のステップ