Skip to main content
AdCP は全オペレーションで一貫したエラーハンドリングを行います。エラー分類を理解し、適切な復旧戦略を実装することが堅牢な統合には不可欠です。

準拠レベル

セラーはエラーハンドリングを段階的に採用できます。各レベルは前のレベルの上に構築されます:
Level実装する内容エージェントができること
Level 1すべてのエラーに codemessage を返すエラーコードで失敗を分類できる
Level 2recoveryretry_afterfieldsuggestion を追加する一時的エラーの自動リトライと修正可能なエラーの自己訂正ができる
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 スキーマで定義されています:
FieldTypeRequiredDescription
codestringYes標準コードまたはセラー固有の機械判読用エラーコード
messagestringYes人向けのエラー説明
recoverystringNoエージェントの復旧分類: transientcorrectableterminal
retry_afternumberNoリトライまでの待機秒数(一時的エラー)
fieldstringNoエラーの原因フィールドパス(例: packages[0].targeting
suggestionstringNoエラーの修正提案
detailsobjectNo追加のコンテキスト情報

標準エラーコード

AdCP は error-code.json に 20 の標準エラーコードを定義しています。セラーはプラットフォーム固有エラーにこの語彙外のコードを使用してもよいです。エージェントは未知のコードを recovery 分類にフォールバックして処理しなければなりません。

認証とアクセス

CodeRecoveryDescriptionResolution
AUTH_REQUIREDcorrectable認証が必要、または認証情報が無効auth ヘッダーで認証情報を提供する
ACCOUNT_NOT_FOUNDterminalアカウント参照を解決できないlist_accounts で確認するか、セラーに連絡する
ACCOUNT_SETUP_REQUIREDcorrectable使用前にアカウントのセットアップが必要details.setup の URL または手順を確認する
ACCOUNT_AMBIGUOUScorrectable自然キーが複数のアカウントに解決する明示的な account_id またはより具体的な自然キーを渡す
ACCOUNT_PAYMENT_REQUIREDterminal未払い残高の支払いが必要バイヤーが請求を解決しなければなりません
ACCOUNT_SUSPENDEDterminalアカウントが停止されているセラーに連絡して解決する

リクエストバリデーション

CodeRecoveryDescriptionResolution
INVALID_REQUESTcorrectableリクエストが不正またはスキーマ制約に違反しているリクエストパラメーターを確認して修正する
UNSUPPORTED_FEATUREcorrectableこのセラーがサポートしていない機能を要求しているget_adcp_capabilities を確認してサポートされていないフィールドを削除する
POLICY_VIOLATIONcorrectableリクエストがコンテンツまたは広告ポリシーに違反しているエラー詳細のポリシー要件を確認する
COMPLIANCE_UNSATISFIEDcorrectable必要な開示事項をターゲットフォーマットで満たせない必要な開示機能をサポートするフォーマットを選択する

インベントリと商品

CodeRecoveryDescriptionResolution
PRODUCT_NOT_FOUNDcorrectable参照した商品 ID が不明または期限切れ無効な ID を削除するか、get_products で再探索する
PRODUCT_UNAVAILABLEcorrectable商品が売り切れまたは利用不可別の商品を選択する
PROPOSAL_EXPIREDcorrectable参照したプロポーザルの expires_at が過ぎているget_products を実行して新しいプロポーザルを取得する
AUDIENCE_TOO_SMALLcorrectableオーディエンスセグメントが最小サイズを下回っているターゲティングを広げるか、より多くのオーディエンスメンバーをアップロードする

予算とクリエイティブ

CodeRecoveryDescriptionResolution
BUDGET_TOO_LOWcorrectable予算がセラーの最小値を下回っている予算を増やすか capabilities.media_buy.limits を確認する
BUDGET_EXHAUSTEDterminalアカウントまたはキャンペーン予算を使い切ったバイヤーが資金を追加するか予算上限を増やさなければなりません
CREATIVE_REJECTEDcorrectableクリエイティブがコンテンツポリシーレビューに不合格セラーの advertising_policies に従って修正する

システム

CodeRecoveryDescriptionResolution
RATE_LIMITEDtransientリクエストレートを超過したretry_after 秒待ってからリトライする
SERVICE_UNAVAILABLEtransientセラーサービスが一時的に利用不可指数バックオフでリトライする
CONFLICTtransient同時変更が検出されたリソースを再読み込みして最新状態でリトライする

復旧分類

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;
}

ベストプラクティス

  1. まず recovery を確認する — エラーの処理方法として最も信頼できるシグナルです
  2. リトライを実装する — 一時的エラーは指数バックオフを使用します
  3. レート制限を尊重するretry_after の値を順守します
  4. 未知のコードを適切に扱うrecovery 分類にフォールバックします
  5. コンテキスト付きログ — デバッグ用に coderecoveryfield を含めます
  6. フォールバックを用意する — 常に代替策を持ちます(例: Webhook 失敗時のポーリング)
  7. terminal エラーはリトライしない — 人のオペレーターにエスカレートします
  8. 部分成功に対応する — 成功レスポンスの警告も処理します

次のステップ

  • Transport Bindings: エラーが MCP と A2A でどう伝達されるかは Transport Errors
  • Task Lifecycle: ステータス処理は Task Lifecycle
  • Webhooks: Webhook エラー処理は Webhooks
  • Security: 認証エラーは Security