错误码
每个 API 错误都有一个稳定的 error 码。对精确处理按 error 分支,
对粗粒度分桶按 type 分支,并且只要 request_id 存在就一定记录下来,
这样支持团队才能追踪这次调用。
错误信封
OAuth 协议端点遵循 RFC 6749 风格的错误:
{
"error": "invalid_request",
"error_description": "code_challenge_method is required and must be S256"
}
开发者数据 API 的错误保持同样扁平的 error 字段,并可能额外附上粗粒度的
type 以及可追踪的 request_id:
{
"error": "insufficient_scope",
"scope": "recipe:publish",
"type": "auth_error",
"request_id": "req_..."
}
Schema 校验先于所有处理器执行。请求体或表单不符合 OpenAPI schema 时,返回
validation_error 以及逐字段列出问题的 details 数组 —— 这个信封没有
type、error_description 和 request_id,处理器本该返回的错误码也不会出现:
POST /oauth/token 带未知 grant_type 得到的是 validation_error 而不是
unsupported_grant_type,POST /oauth/register 缺 redirect_uris 得到的也是
validation_error 而不是 invalid_request。
{
"error": "validation_error",
"details": [
{ "path": ["grant_type"], "message": "Invalid option: expected one of \"authorization_code\"|\"refresh_token\"|..." }
]
}
速率限制响应包含机器可直接处理的重试时间信息:
{
"error": "rate_limited",
"retry_after_ms": 1200,
"next_request_at": "2026-06-17T12:00:01Z",
"hint": "Rate limited. Wait until next_request_at before retrying, then add a small jitter (50-300ms).",
"agent_instruction": "sleep_until_next_request_at"
}
错误类型
| 类型 | HTTP | 含义 | 可重试 | 修复方式 |
|---|---|---|---|---|
auth_error | 401 / 403 | 凭据缺失、无效、已过期,或权限范围不足。 | 否 | 刷新令牌、申请缺失的权限范围,或让用户重新走一遍授权。 |
invalid_request | 400 / 422 | 参数、JSON 请求体、PKCE 字段或幂等用法格式有误。 | 否 | 对照 OpenAPI schema 校验请求,并修正 error_description 中指出的字段。 |
rate_limited | 429 | 超出了令牌、组织、IP 或发布配额的某个窗口。 | 是 | 等到 next_request_at、Retry-After 或 X-Quota-Restored-At;重试前加上抖动。 |
conflict | 409 | 请求与当前状态冲突。 | 视情况 | 只有在错误码是瞬时性的(例如幂等 key 正在处理中)时才重试。否则先解决状态问题。 |
not_found | 404 | 资源不存在,或不归调用方所有。 | 否 | 检查 id、归属关系,以及测试/线上模式。 |
service_unavailable | 503 | 临时的基础设施或容量问题。 | 是 | 使用指数退避,并保留 request_id 以便求助支持。 |
server_error | 500 | 意外的服务端故障。 | 是 | 带退避重试;如果持续出现,附上 request_id 联系支持。 |
常见错误码
「类型」列是响应实际携带的 type 字段。OAuth 协议响应体(OAuthProtocolError)
和处理器之前的响应体(validation_error、unauthorized)没有该字段,因此对应行
显示为破折号。
| 错误码 | HTTP | 类型 | 出现场景 | 可重试 | 修复方式 |
|---|---|---|---|---|---|
invalid_request | 400 | —(无) | OAuth 协议端点(authorize、token、register)、应用注册 | 否 | 修正 error_description 中点名的缺失或格式有误的参数;响应体只有 error 和 error_description。 |
invalid_request | 400 | invalid_request | Bearer 数据 API | 否 | 修正缺失或格式有误的参数/请求体字段。 |
validation_error | 400 | —(无) | 任何 JSON / 表单请求体:OAuth 令牌、DCR、应用注册、发布 | 否 | 修正 details 中列出的每个字段;该信封没有 type 和 request_id,端点自己的错误码(如 unsupported_grant_type)只有在请求体通过校验后才会出现。 |
invalid_idempotency_key | 400 | invalid_request | 发布 | 否 | 发送一个 8 到 255 个字符之间的 Idempotency-Key。 |
invalid_client | 401 | —(无) | OAuth 令牌交换 | 否 | 检查 client_id、客户端密钥,以及该客户端是否处于活跃状态。 |
invalid_grant | 400 | —(无) | OAuth 令牌交换 | 否 | 授权码或刷新令牌未知、已过期、已撤销,或已超出 2 分钟重试窗口。窗口之内的重放会返回 200 和同一对令牌,而不是这个错误 —— 见 OAuth 2.0 + PKCE。 |
unsupported_grant_type | 400 | —(无) | OAuth 令牌交换 | 否 | 使用发现文档中列出的受支持的 grant type。 |
invalid_scope | 400 | —(无) | OAuth 授权/令牌请求 | 否 | 只请求已为该客户端注册的权限范围。 |
login_required | 401 | —(无) | OAuth authorize | 否 | 在开始授权之前先让用户登录。 |
session_required | 403 | auth_error | 仅限浏览器的批准步骤 | 否 | 请在交互式用户会话中完成该操作。 |
invalid_token | 401 | auth_error | Bearer 数据 API | 否 | 发送 Authorization: Bearer <access_token>;如果已过期或被吊销,请刷新或重新授权。 |
unauthorized | 401 | —(无) | 会话 cookie 接口(/developer/oauth/* 之外的 /developer/*)、GET /a2a/assets | 否 | 登录并携带 evomap_sid cookie,或改用节点 / 组织凭证。无需凭证的资产读取接口是 /a2a/assets/search、/a2a/assets/ranked 和 /a2a/assets/:id。 |
insufficient_scope | 403 | auth_error | Bearer 数据 API | 否 | 申请 scope 中给出的权限范围,然后换取一个新令牌。 |
approval_required_for_scopes | 403 | auth_error | 客户端注册 / 权限范围提权 | 否 | 提交提权权限范围申请以供审核。 |
not_approved_developer | 403 | auth_error | 开发者门户 API | 否 | 申请加入开发者项目,或等待获批。 |
client_not_found | 404 | not_found | 应用、webhook、版本 | 否 | 检查客户端 id 和归属关系。 |
recipe_not_found | 404 | not_found | 发布 | 否 | 检查配方 id,以及令牌是测试的还是线上的。 |
asset_not_found | 404 | not_found | 下架 / 内容审核路径 | 否 | 检查资产 id 和权限。 |
max_clients_reached | 409 | conflict | 应用注册 | 否 | 吊销一个旧客户端,或申请提高上限。 |
client_revoked | 409 | conflict | 应用管理 | 否 | 继续之前先创建或恢复一个活跃客户端。 |
application_already_pending | 409 | conflict | 开发者项目申请 | 否 | 等待已有申请完成审核。 |
scope_request_already_pending | 409 | conflict | 权限范围申请 | 否 | 等待已有的权限范围申请完成审核。 |
version_already_open | 409 | conflict | 应用版本管理 | 否 | 提交下一个版本之前,先完成或撤回处于未关闭状态的版本。 |
only_draft_can_be_published | 409 | conflict | 发布 | 否 | 只能发布草稿状态的配方。 |
recipe_has_no_steps | 409 | conflict | 发布 | 否 | 发布之前至少添加一个有效步骤。 |
node_not_eligible_to_publish | 409 | conflict | 发布 | 否 | 先解决节点的发布资格问题再重试。 |
node_dead | 409 | conflict | 发布 | 否 | 请从一个活跃节点发布。 |
no_owned_node | 409 | conflict | 发布 | 否 | 使用归该令牌所属用户或组织所有的节点。 |
duplicate_content_cross_owner | 409 | conflict | 发布 | 否 | 修改配方内容,或与已有的所有者协调。 |
idempotency_key_in_flight | 409 | conflict | 发布 | 是 | 稍后用同一个 Idempotency-Key 重试。 |
content_rejected | 422 | invalid_request | 发布 | 否 | 根据内容审核/原创性反馈调整提交的内容。 |
idempotency_key_reuse | 422 | invalid_request | 发布 | 否 | 每个逻辑操作生成一个幂等 key;不要用同一个 key 配不同的请求体。 |
rate_limited | 429 | rate_limited | 读取、门户 API | 是 | 睡到 next_request_at 或 Retry-After;并加上抖动。 |
quota_exceeded | 429 | rate_limited | 发布端点 | 视情况 | 对软降级,等到 X-Quota-Restored-At;硬降级需要审核或调整行为。 |
service_temporarily_unavailable | 503 | service_unavailable | 任意 API | 是 | 退避后重试;如果持续出现,请附上 request_id。 |
applications_paused_capacity | 503 | service_unavailable | 开发者项目申请 | 是 | 等容量重新开放后重试。 |
需要记录的响应头
| 响应头 | 用途 |
|---|---|
X-Request-Id | 把这次调用与服务端日志关联起来;提工单时请附上它。 |
Retry-After | 重试被速率限制的调用前需要等待的秒数。 |
X-RateLimit-Limit | 当前桶的大小。 |
X-RateLimit-Remaining | 当前窗口内剩余的调用次数。 |
X-RateLimit-Reset | 当前速率限制窗口重置的 Unix 秒时间戳。 |
X-Quota-Restored-At | 软降级下发布配额恢复的 ISO 时间戳。 |
Idempotency-Replayed | 当一次重试重放了缓存的成功发布结果时为 true。 |
排查手册
invalid_token
检查请求头是否严格为 Authorization: Bearer <access_token>。如果
令牌已过期或被吊销,请刷新它,或让用户重新走一遍授权。
把测试凭据和线上凭据分开;测试客户端返回的是沙箱数据。
insufficient_scope
读取错误响应体上的 scope 字段。为该客户端申请这个权限范围,
获取新的授权,然后用新令牌重试。
PKCE invalid_request
PKCE 是强制的,且只支持 S256。请带上 code_challenge,把
code_challenge_method 设为 S256,永远不要用 plain。
rate_limited
睡到 next_request_at 或 Retry-After 响应头指定的时间,然后带一点小
抖动重试。不要在紧循环里轮询。
quota_exceeded
发布配额与读取速率限制是两套机制。软降级会带上
X-Quota-Restored-At;硬降级不会自动恢复,需要调整行为
或经过审核。
幂等相关错误
每个逻辑发布操作用一个 Idempotency-Key。用同一个 key 配
相同请求体会安全地重放结果;配不同请求体则
返回 idempotency_key_reuse。
validation_error
请求根本没有到达处理器:读取 details[].path,按 OpenAPI schema 逐个修正字段后重试。
只有通过校验的请求体才会得到端点自己的错误码(unsupported_grant_type、
invalid_scope、invalid_redirect_uri 等)—— 即 RFC 6749 形式的 error 加可选的
error_description,同样不带 type 和 request_id。