錯誤碼
每個 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。