错误参考
稳定的平台错误码与处理方式。
先识别错误来源
解释 HTTP 状态前,先读取 X-System-One-Error-Source 和 X-System-One-Error-Code。upstream 表示保留上游有效 JSON 错误的原始 400–599 状态与正文。正文可能是 detail 数组、字符串或上游自己的错误对象,不一定采用平台结构。例如,透传的校验错误可以是:
{
"detail": [
{ "loc": ["body", "questions"], "msg": "Invalid question definition", "type": "value_error" }
]
}这只是格式示例,不是真实失败记录。上游提供的 Retry-After 秒数或 HTTP 日期会原样保留。上游 401/403 仍为 401/403,400/422 仍为 400/422,429/529 仍为 429/529。上游认证失败不代表浏览器登录过期;即便来源为 platform,provider_* 错误也可能是经过安全替换的上游认证失败,不应把用户引导至重新登录。
稳定分类头对上游 429/529 使用 provider_busy,400/422 使用 provider_rejected_request,其他上游 HTTP 失败使用 provider_unavailable。原始正文可能有不同错误码。UI 应根据响应头分类显示本地化提示,不直接展示上游 message 或 detail。错误正文可能回显输入,不应无差别记录。
平台错误结构
X-System-One-Error-Source: platform 表示本地错误或安全替换后的错误。System One 本地错误使用以下结构;支付端点仍沿用 TinyShip 的支付契约。以下数值仅为示例:
{
"error": {
"code": "insufficient_credits",
"message": "This request requires 3 credits. Top up your account to continue.",
"request_id": "example-request-id"
},
"request_id": "example-request-id"
}优先使用响应头错误码,平台正文也包含 error.code。平台账本 ID 从 X-Request-Id 读取,可选上游 ID 从 X-Upstream-Request-Id 读取。上游正文中的 request_id 不是平台标识。不要把认证头或敏感请求内容写入错误日志。
项目 SDK @system-one-ai/sdk@0.3.0 使用独立错误命名空间:APIError 保留状态、请求 ID 和重试延迟,但主动丢弃服务端正文。平台分类请从 Fetch 原始响应头读取。官方 @typesafe-ai/sdk@0.6.0 使用自己的错误类;不要假定两个客户端有相同错误结构,也不要直接记录整个错误对象。
认证与输入
| HTTP | 错误码 | 含义与处理 |
|---|---|---|
| 400 | invalid_json | 缺少 JSON、格式错误、UTF-8 无效或对象键重复(包括转义名称)。请修正请求体。 |
| 401 | invalid_api_key | Bearer 密钥缺失、无效或已撤销。使用有效的平台密钥。 |
| 401 | unauthorized | 当前端点需要有效账户或会话。请登录。 |
| 403 | account_disabled | 账户被停用,请联系部署运营者。 |
| 403 | invalid_origin | 浏览器修改请求不是来自已配置的应用源。 |
| 404 | not_found | 端点不存在,检查主应用地址与路径。 |
| 404 | key_not_found | 密钥不存在或不属于当前账户。 |
| 405 | method_not_allowed | 使用该端点文档指定的 HTTP 方法。 |
| 413 | request_too_large | 原始请求体超过 65,536 字节,减少状态或问题内容。 |
| 415 | unsupported_media_type | 设置 Content-Type: application/json。 |
| 422 | invalid_request | 字段、问题、名称、数量或嵌套深度无效。 |
| 422 | invalid_idempotency_key | 使用允许的幂等键格式。 |
认证与本地准入校验失败发生在新的推理积分预扣之前,不会消耗新决策积分。预扣后收到的上游拒绝会触发退款;已有操作不受影响。
积分、并发与限制
| HTTP | 错误码 | 含义与处理 |
|---|---|---|
| 402 | insufficient_credits | 余额不足,充值或减少本次请求规模。 |
| 409 | key_limit_exceeded | 已有 20 个有效密钥,先撤销不再使用的密钥。 |
| 409 | idempotency_conflict | 同一账户曾用该键提交不同的规范化请求体。 |
| 409 | request_in_progress | 原请求处理中,按 Retry-After 等待后使用相同键和请求体。 |
| 409 | idempotency_failed | 原操作失败且已返还积分,新尝试需要新键。 |
| 409 | request_expired | 预扣记录在完成前已终止并退款。 |
| 410 | idempotency_expired | 保留记录中已无可重放数据,核对状态后再发起新操作。 |
| 429 | rate_limit_exceeded | 按 Retry-After 等待,默认每账户每分钟 120 次决策请求。 |
过期键可能已经移除,因此不一定返回 410。超过 24 小时后,不应继续依赖旧键防止重复请求。
上游与基础设施
| HTTP | 错误码 | 含义与处理 |
|---|---|---|
| 503 | provider_not_configured | 上游凭据或地址未正确配置,没有进行新的积分预扣。 |
| 503 | app_not_configured | 浏览器应用源未正确配置,请运营者修正 APP_BASE_URL。 |
| 429 / 529 | provider_busy | 原始上游状态;预扣积分会返还,遵循原始 Retry-After。 |
| 400 / 422 | provider_rejected_request | 原始上游状态;确认已记录退款后修正请求。 |
| 其他上游 4xx / 5xx | provider_unavailable | 保留原始状态,包括上游 401/403 与 500;核对来源,必要时联系运营者。 |
| 502 | invalid_provider_response | 无效或含敏感信息的成功响应、无效 JSON / UTF-8、超过 1 MiB 或上游重定向;不生成默认答案。 |
| 503 | provider_configuration_error | 显式适配器配置需要修正。 |
| 502 | provider_unavailable | 获取可用上游响应前发生传输失败。 |
| 504 | provider_timeout | 上游响应头及正文共用的 20 秒期限到期。 |
| 499 | request_cancelled | 调用者取消了请求。 |
| 501 | model_listing_not_supported | 所选 OpenRouter 上游不提供此 TypeSafe 原生模型列表端点。 |
| 503 | request_reconciliation_pending | 即时退款对账失败,扣费头省略;请保留请求 ID 并检查原操作。 |
| 500 | internal_error | 服务出现意外错误,请保留请求 ID,核对状态后再新建操作。 |
网关先返还决策预扣,再返回普通上游错误。X-System-One-Credits: 0 表示本次没有保留扣费;缺失该头不等于零。若即时退款对账失败,优先返回 503 request_reconciliation_pending,并省略扣费头。模型列表请求始终不预扣决策积分。
安全例外与重试恢复
上游错误包含运营者完整凭据时,包括 JSON 转义后解码出的同一凭据,正文会替换为安全平台错误,同时保留上游状态。来源改为 platform,仍提供稳定的上游错误分类,UI 不显示上游错误原文。包含凭据的成功响应会被拒绝并返回 502。无效 JSON、非 JSON、无效 UTF-8、超限响应与上游重定向也返回 502 invalid_provider_response;不会跟随重定向。
只转发文档列出的安全跟踪、限流头,以及 Content-Type 和 Retry-After;包含密钥的头值会被省略。Cookie、认证头和 Location 不会转发。因此,网关并不承诺无条件逐字节转发所有响应。
已记录的上游失败再次使用原键时会返回 idempotency_failed。只有失败并退款的结果明确后,才适合使用新键重新尝试。客户端连接超时并不能证明服务器失败,应先用原键恢复。Worker 不自动重试上游,SDK 的重试设置是独立的。