# 錯誤

> 了解所有 Kanorio API 回傳的統一錯誤格式、錯誤代碼與 HTTP 狀態碼的對應關係，以及如何處理重試機制。

> Markdown version of https://kanorio.com/zh-Hant/developers/basics/errors. Append `.md` to any developer docs URL for this format. Index: https://kanorio.com/llms.txt · MCP: https://app.kanorio.com/api/mcp
>
> Translated from the English original: https://kanorio.com/en/developers/basics/errors.md

Management API、Content Delivery API 與 Customer API 回傳的錯誤格式皆相同。每個錯誤代碼在所有 API 中皆對應相同的 HTTP 狀態碼，因此您只需編寫一個錯誤處理器即可適用於所有 API。

```json
{
  "error": {
    "code": "SLUG_TAKEN",
    "message": "Another post already uses this slug.",
    "details": { "slug": "how-we-feed-our-starter" }
  }
}
```

| 欄位 | 類型 | 備註 |
|---|---|---|
| `error.code` | string | 穩定且機器可讀。請根據此欄位進行分支處理。 |
| `error.message` | string | 供日誌與開發者參考的英文句子。內容隨時可能變更，請勿依此進行比對。 |
| `error.details` | object, optional | 代碼的結構化上下文，例如 `retryAfter`、`availableAt`、`issues` 或 `requiredScope`。 |

成功的回應絕不會包含 `error` 欄位，且 API 不會將成功回應包裹在 `ok` 旗標中。

## 錯誤代碼對應的 HTTP 狀態碼

每個錯誤代碼皆對應一個狀態碼。未來新增的代碼亦遵循相同規則，因此您可以根據狀態碼來處理尚未遇過的錯誤代碼。

| 狀態 | 含義 | 範例 |
|---|---|---|
| `400` | 請求格式錯誤或驗證失敗 | `VALIDATION`, `INVALID_CURSOR`, 以 `INVALID_` 開頭的代碼 |
| `401` | 無憑證，或憑證無效、過期或已撤銷 | `UNAUTHORIZED`, `UNAUTHENTICATED` |
| `403` | 憑證有效但無權存取：權限、計劃、角色或模式不符 | `FORBIDDEN`, `OWNER_REQUIRED`, `LIMIT_REACHED`, `LOCKED` |
| `404` | 此網站上不存在該資源 | `NOT_FOUND`, 以 `_NOT_FOUND` 結尾的代碼 |
| `405` | 此路徑不支援該方法 | `METHOD_NOT_ALLOWED` |
| `409` | 與當前狀態衝突：過時的寫入、網址別名已被佔用、重複資料 | `CONFLICT`, `SLUG_TAKEN`, `IDEMPOTENCY_CONFLICT` |
| `413` | 上傳檔案過大 | `FILE_TOO_LARGE` |
| `422` | 輸入格式正確，但違反業務規則 | `SITE_NOT_PUBLISHED`, `ASSET_IN_USE` |
| `429` | 請求過多或後端繁忙；請稍後重試 | `RATE_LIMITED`, `SEARCH_BUSY`, `ANALYTICS_BUSY` |
| `500` | 未預期的伺服器錯誤 | `INTERNAL` |
| `502`, `503` | 上游服務失敗或暫時無法使用 | `UPLOAD_FAILED`, `SERVICE_UNAVAILABLE`, `SHARED_SENDER_PAUSED` |

API 參考文件中的每項操作皆列出了其可能回傳的錯誤代碼及其含義。OpenAPI 文件中亦包含相同的列表，標記為 `x-kanorio-errors`。

## 驗證錯誤

`VALIDATION` 錯誤會在 `details.issues` 中列出所有驗證失敗的欄位。`path` 欄位使用點號（.）來表示巢狀結構。

```json
{
  "error": {
    "code": "VALIDATION",
    "message": "Input does not match the capability schema.",
    "details": {
      "issues": [
        { "path": "title", "message": "Required", "code": "invalid_type" }
      ]
    }
  }
}
```

Content Delivery API 的表單提交若有無效回答，會以相同方式回報，並將問題 ID 作為 `path`。

## 重試機制

- 針對 `429`、`502` 與 `503` 錯誤，請使用退避演算法（backoff）進行重試。若回應中包含 `Retry-After` 標頭或 `details.retryAfter` 欄位，請至少等待該秒數後再重試。
- 請勿直接重試其他 `4xx` 錯誤，應先修正請求內容。
- 若寫入操作出現 `409 CONFLICT`，代表資源在您讀取後已被他人修改。請重新讀取資源、合併變更，並使用新的時間戳記再次發送寫入請求。
- 請使用 [冪等性鍵值](https://kanorio.com/zh-Hant/developers/basics/idempotency) 確保建立請求可安全重試。

## 權限錯誤

`403` 回應會在 `details` 中說明缺失的權限，例如權杖缺乏的 `requiredScope`，或是當服務帳戶呼叫帳戶層級操作時顯示 `reason: "principal_not_allowed"`。遇到此類錯誤時，請引導使用者進行修正，而非進行重試。

## 請求識別碼

Content Delivery API 與 Customer API 的回應皆包含 `X-Request-Id` 標頭。若需聯絡支援團隊查詢特定請求，請提供此識別碼。

## 錯誤代碼

下表列出了目前各 API 可能回傳的所有錯誤代碼，內容由 OpenAPI 文件自動產生。

### Management API codes

| Code | HTTP status |
| --- | --- |
| `EMPTY_SLUG` | 400 |
| `INVALID_BODY` | 400 |
| `INVALID_CURSOR` | 400 |
| `INVALID_EDITED_PATH` | 400 |
| `INVALID_FORMAT` | 400 |
| `INVALID_ORDER` | 400 |
| `INVALID_SUBDOMAIN` | 400 |
| `UNSUPPORTED_EDITED_PATH` | 400 |
| `VALIDATION` | 400 |
| `UNAUTHENTICATED` | 401 |
| `UNAUTHORIZED` | 401 |
| `CUSTOM_DOMAIN_CHANGE_FORBIDDEN` | 403 |
| `CUSTOM_DOMAIN_LIMIT_REACHED` | 403 |
| `FORBIDDEN` | 403 |
| `LIMIT_REACHED` | 403 |
| `MODERATION_LOCKED` | 403 |
| `OWNER_REQUIRED` | 403 |
| `PLAN_CANNOT_PUBLISH` | 403 |
| `PRO_REQUIRED` | 403 |
| `REDIRECT_LIMIT` | 403 |
| `SUBDOMAIN_CHANGE_FORBIDDEN` | 403 |
| `TOKEN_WEBSITE_SCOPED` | 403 |
| `HEADER_NOT_FOUND` | 404 |
| `HOME_PAGE_NOT_FOUND` | 404 |
| `NOT_FOUND` | 404 |
| `OWNER_NOT_FOUND` | 404 |
| `PAGE_NOT_FOUND` | 404 |
| `PARENT_NOT_FOUND` | 404 |
| `PRODUCT_NOT_FOUND` | 404 |
| `WEBSITE_NOT_FOUND` | 404 |
| `METHOD_NOT_ALLOWED` | 405 |
| `CONFLICT` | 409 |
| `CUSTOM_DOMAIN_TAKEN` | 409 |
| `HAS_CHILDREN` | 409 |
| `HOSTED_ONLY` | 409 |
| `HOSTNAME_CONFLICT` | 409 |
| `IDEMPOTENCY_CONFLICT` | 409 |
| `IDEMPOTENCY_IN_PROGRESS` | 409 |
| `KEY_TAKEN` | 409 |
| `PRIMARY_HEADER_PROTECTED` | 409 |
| `READD_COOLDOWN` | 409 |
| `REDIRECT_CONFLICT` | 409 |
| `SLUG_TAKEN` | 409 |
| `SUBDOMAIN_TAKEN` | 409 |
| `TAKEN` | 409 |
| `BLOCKED` | 422 |
| `BREADCRUMB_PARENT_INVALID` | 422 |
| `CONTENT_VALIDATION_FAILED` | 422 |
| `CORE_SECTION_VIOLATION` | 422 |
| `CUSTOM_DOMAIN_BLOCKED` | 422 |
| `CUSTOM_DOMAIN_HIGH_RISK_TLD` | 422 |
| `CUSTOM_DOMAIN_TOO_LONG` | 422 |
| `DEPTH_EXCEEDED` | 422 |
| `DNS_NOT_PROPAGATED` | 422 |
| `DOMAIN_CHANGE_NOT_ALLOWED` | 422 |
| `EMPTY_CONTENT` | 422 |
| `FOREIGN_PAGE` | 422 |
| `HIGH_RISK_TLD` | 422 |
| `HOME_HAS_HIERARCHY` | 422 |
| `HOME_NOT_CREATABLE` | 422 |
| `HOME_NOT_DELETABLE` | 422 |
| `HOME_NOT_SORTABLE` | 422 |
| `HOME_SLUG_IMMUTABLE` | 422 |
| `LINK_IN_BIO_DEDICATED_ENTRY` | 422 |
| `LINK_IN_BIO_NOT_DUPLICABLE` | 422 |
| `META_NOT_LIVE` | 422 |
| `NO_PUBLIC_URL` | 422 |
| `OWNERSHIP_REQUIRED` | 422 |
| `PAGE_LIMIT_REACHED` | 422 |
| `PARENT_NOT_ALLOWED` | 422 |
| `RESERVED_SEGMENT` | 422 |
| `RESERVED_SLUG` | 422 |
| `RESERVED_SUBDOMAIN` | 422 |
| `SANITIZE_FAILED` | 422 |
| `SEARCH_CONSOLE_AUTH_EXPIRED` | 422 |
| `SEARCH_CONSOLE_NOT_CONNECTED` | 422 |
| `SEARCH_CONSOLE_PERMISSION_DENIED` | 422 |
| `SEARCH_CONSOLE_PROPERTY_NOT_FOUND` | 422 |
| `SEGMENT_TOO_LONG` | 422 |
| `SITEMAP_NOT_REACHABLE` | 422 |
| `SUBDOMAIN_MISSING` | 422 |
| `SYSTEM_PAGE_LOCKED` | 422 |
| `TEMPLATE_PRODUCT_LOCKED` | 422 |
| `ANALYTICS_BUSY` | 429 |
| `DAILY_LIMIT_REACHED` | 429 |
| `RATE_LIMITED` | 429 |
| `SEARCH_BUSY` | 429 |
| `SEARCH_CONSOLE_QUOTA_EXCEEDED` | 429 |
| `STOCK_BUSY` | 429 |
| `INTERNAL` | 500 |
| `SEARCH_CONSOLE_ERROR` | 502 |
| `BUSY` | 503 |
| `SEARCH_CONSOLE_UNAVAILABLE` | 503 |
| `SERVICE_UNAVAILABLE` | 503 |
| `WRITE_FROZEN` | 503 |

### Content Delivery API codes

| Code | HTTP status |
| --- | --- |
| `BAD_HOST` | 400 |
| `FILE_TYPE_NOT_ALLOWED` | 400 |
| `INVALID_ANSWERS` | 400 |
| `INVALID_BODY` | 400 |
| `INVALID_CURSOR` | 400 |
| `INVALID_KIND` | 400 |
| `INVALID_LIMIT` | 400 |
| `INVALID_PATH` | 400 |
| `INVALID_QUERY` | 400 |
| `QUERY_REQUIRED` | 400 |
| `QUERY_TOO_LONG` | 400 |
| `QUERY_TOO_SHORT` | 400 |
| `TOO_MANY_PARAMS` | 400 |
| `UNKNOWN_BOT` | 400 |
| `UNSUPPORTED_LOCALE` | 400 |
| `VALIDATION` | 400 |
| `VERIFICATION_EXPIRED` | 400 |
| `NOT_PREVIEW` | 403 |
| `COLLECTION_NOT_FOUND` | 404 |
| `FORM_NOT_FOUND` | 404 |
| `HEADER_NOT_FOUND` | 404 |
| `ITEM_NOT_FOUND` | 404 |
| `PAGE_NOT_FOUND` | 404 |
| `PRODUCT_NOT_FOUND` | 404 |
| `SITE_NOT_FOUND` | 404 |
| `UNIT_NOT_FOUND` | 404 |
| `DUPLICATE_SUBMISSION` | 409 |
| `FILE_TOO_LARGE` | 413 |
| `RATE_LIMITED` | 429 |
| `SEARCH_BUSY` | 429 |
| `QUERY_TIMEOUT` | 503 |
| `UPLOAD_UNAVAILABLE` | 503 |
| `UPSTREAM_UNAVAILABLE` | 503 |

### Customer API codes

| Code | HTTP status |
| --- | --- |
| `BAD_HOST` | 400 |
| `INVALID_CODE` | 400 |
| `INVALID_CURSOR` | 400 |
| `INVALID_EMAIL` | 400 |
| `INVALID_LIMIT` | 400 |
| `VALIDATION` | 400 |
| `UNAUTHENTICATED` | 401 |
| `BOT_PROTECTION_FAILED` | 403 |
| `DOWNLOAD_NOT_ALLOWED` | 403 |
| `EXPIRED` | 403 |
| `FORBIDDEN` | 403 |
| `LIMIT_REACHED` | 403 |
| `LOCKED` | 403 |
| `NOT_RELEASED` | 403 |
| `NO_ATTEMPTS_LEFT` | 403 |
| `REVIEWS_DISABLED` | 403 |
| `CERTIFICATE_DISABLED` | 404 |
| `DELIVERABLE_NOT_FOUND` | 404 |
| `NOT_FOUND` | 404 |
| `NO_FILE` | 404 |
| `NO_RECEIPT` | 404 |
| `ORDER_NOT_FOUND` | 404 |
| `SITE_NOT_FOUND` | 404 |
| `UNIT_NOT_FOUND` | 404 |
| `WEBSITE_NOT_FOUND` | 404 |
| `METHOD_NOT_ALLOWED` | 405 |
| `ALREADY_REVIEWED` | 409 |
| `FILE_NOT_PROTECTED` | 409 |
| `NOT_A_QUIZ` | 409 |
| `QUIZ_NOT_READY` | 409 |
| `ACCOUNT_BLOCKED` | 422 |
| `ALREADY_REGISTERED` | 422 |
| `CHALLENGE_EXPIRED` | 422 |
| `EMAIL_NOT_VERIFIED` | 422 |
| `METHOD_DISABLED` | 422 |
| `VERIFY_FAILED` | 422 |
| `RATE_LIMITED` | 429 |
| `SHARED_SENDER_PAUSED` | 503 |
| `SIGN_FAILED` | 503 |
| `UPSTREAM_UNAVAILABLE` | 503 |
