# 错误

> 了解所有 Kanorio API 返回的统一错误格式、错误代码与 HTTP 状态码的对应关系，以及如何处理重试机制。

> Markdown version of https://kanorio.com/zh-CN/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-CN/developers/basics/idempotency) 确保创建请求可安全重试。

## 权限错误

`403` 响应会在 `details` 中说明缺失的权限，例如令牌缺乏的 `requiredScope`，或是当服务账号调用账号层级操作时显示 `reason: "principal_not_allowed"`。遇到此类错误时，请引导用户进行修正，而非进行重试。

## 请求 ID

Content Delivery API 和 Customer API 的响应均包含 `X-Request-Id` 标头。若需联系支持团队查询特定请求，请提供此 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 |
