龍騰 AI 非選批閱平台 開發者文件中心
維運與疑難排解

錯誤處理策略

錯誤回應物件、錯誤結構化資料、重試判準,以及客戶端處理樣板

錯誤回應物件

{
  "error": {
    "code": "ERR_NOT_FOUND",
    "message": "Exam 'exam_9x8y7z' not found",
    "details": {
      "resource_type": "exam",
      "resource_id": "exam_9x8y7z"
    },
    "request_id": "req_01j9f3k2m1"
  }
}
欄位說明
code錯誤識別碼
message錯誤說明
details選填的結構化脈絡資料,形態依代碼而定(見下節)
request_id伺服器指派的請求 id

發生錯誤時,請記錄該請求回傳的 request_id,以提供技術支援追蹤查詢。

錯誤結構化資料

錯誤物件中的 details,依錯誤類別採用其中一種形態。

驗證錯誤

適用 ERR_VALIDATION_FAILED 錯誤,於請求內容不符合驗證條件時產生,錯誤結構化資料內容為陣列:

{
  "error": {
    "code": "ERR_VALIDATION_FAILED",
    "message": "Request validation failed",
    "details": [
      { "field": "responses[0].answer", "message": "Answer is required" }
    ],
    "request_id": "req_01j9f3k2m1"
  }
}

field 為出錯輸入的 JSON 路徑。

脈絡錯誤

適用其餘所有錯誤代碼,採物件形態:

{
  "error": {
    "code": "ERR_EXAM_LOCKED",
    "message": "Exam content is immutable; create a new exam instead",
    "details": { "exam_id": "exam_abc123", "fields": ["questions"] },
    "request_id": "req_01j9f3k2m1"
  }
}

重試請求

依照錯誤代碼類別,部分錯誤的請求可以重試。

類別代碼處理方式
可重試,定時ERR_RATE_LIMIT_EXCEEDEDERR_SERVICE_UNAVAILABLE等待 Retry-After 標頭或 details.retry_after 秒數後重試
可重試,退避ERR_TIMEOUTERR_INTERNAL、非 JSON 的 502 / 504指數退避,限制嘗試次數
不可重試,應修正請求ERR_VALIDATION_FAILEDERR_INVALID_JSONERR_CONTENT_TOO_LONGERR_BATCH_TOO_LARGEERR_PAYLOAD_TOO_LARGEERR_UNSUPPORTED_MEDIA_TYPE修正輸入;原樣重送的結果不會改變
不可重試,應修正憑證ERR_UNAUTHORIZEDERR_API_KEY_DISABLEDERR_FORBIDDENERR_INVALID_SCOPE調整金鑰、scope 或來源網段
不可重試,應檢查狀態ERR_NOT_FOUNDERR_CONFLICTERR_DUPLICATEERR_DUPLICATE_TXIDERR_EXAM_LOCKEDERR_EXAM_ARCHIVED重新讀取資源並解決狀態問題,不要盲目重試

客戶端處理樣板

async function callNorma(
  url: string,
  init: RequestInit,
  attempt = 0,
): Promise<unknown> {
  const res = await fetch(url, init);
  if (res.ok) return res.json();

  const { error } = await res.json();

  switch (error.code) {
    case 'ERR_RATE_LIMIT_EXCEEDED':
    case 'ERR_SERVICE_UNAVAILABLE': {
      const wait = Number(
        res.headers.get('Retry-After') ?? error.details?.retry_after ?? 60,
      );
      await sleep(wait * 1000);
      return callNorma(url, init, attempt + 1); // 沿用同一把 Idempotency-Key
    }
    case 'ERR_TIMEOUT':
    case 'ERR_INTERNAL':
      if (attempt < 3) {
        await sleep(2 ** attempt * 1000);
        return callNorma(url, init, attempt + 1); // 沿用同一把 Idempotency-Key
      }
      throw new ApiError(error);
    case 'ERR_VALIDATION_FAILED':
      throw new ValidationError(error.details); // [{ field, message }]
    case 'ERR_DUPLICATE_TXID':
      // 客戶端問題:同鍵不同內容,不可視為成功
      throw new ApiError(error);
    default:
      throw new ApiError(error); // error.request_id 供記錄與技術支援使用
  }
}

在上述範例中,init 在各次重試之間必須完全不變,包含 Idempotency-Key 標頭,以確保事件重送及重播正常。

相關

本頁內容