Incarna文档GitHub控制台

错误

所有错误响应都用同一个信封:

{ "error": { "code": "not_found", "message": "agent 00000000-... not found" } }

code 是一个稳定的、机器可读的字符串,你可以直接对它分支。它刻意不是 HTTP 状态码数字: 状态码已经在状态行里了,而且它无法区分两个共用同一状态码的不同失败。422 同时覆盖了 请求体格式错误和幂等键被复用,这两者需要不同的处理。

message 是给人和日志看的。不要解析它。

错误码

HTTP code 含义 该怎么办
400 bad_request 被我们或上游供应商拒绝。message 里带着供应商自己的原文。 读 message。供应商的容量与策略错误都落在这里。
401 unauthorized 凭据缺失、格式错误、无效或已撤销。 检查 bearer。已撤销的密钥与错误的密钥不可区分,这是有意的。
403 forbidden 已认证,但无权限。 最常见的是:用 API 密钥去签发 API 密钥。请改用控制台会话。
404 not_found 在你的组织里没有这个资源。 属于另一个租户的资源同样返回 404 —— 不透露存在性。
405 method_not_allowed 动词用错了。
409 idempotency_in_progress 带这个 Idempotency-Key 的请求仍在执行。 短暂退避后重试。不要改动请求体。
422 validation_error 请求体校验失败。 修正请求。
422 idempotency_key_reuse 同一个键,不同的请求体。 一个键只绑定一个请求。换一个新键。
422 idempotency_key_invalid 键格式错误。
429 rate_limited 该组织超过每分钟 120 次请求。 退避。按组织计,所以多申请几把密钥没有用。
500 internal_error 我们的问题。 退避重试。若持续存在请反馈。
503 unavailable 某个必需的子系统未启用。 付费接口未配置时 x402 路由返回它 —— 关闭,而不是免费。

402 不是错误

/x402/* 路由上的 402 是协议在正常工作。它携带的是可签名的支付条款,不是一次失败。 见 x402

几个值得注意的行为

404 隐藏存在性。 请求一个属于另一个组织的 agent,返回的 404 和请求一个从未存在过的 agent 完全一样。这是有意为之:区分这两者就等于让任何人都能枚举其他租户的资源。

已撤销和无效是同一个 401。 出于同样的理由。

上游错误不做洗白。 当邮件供应商拒绝创建收件箱时,你会拿到 400 和供应商的原始信息。 另一种做法 —— 把它吞掉并存下一个失效地址 —— 会在更晚、更远、更难诊断的地方失败。

记账从不导致请求失败。 如果一笔支付已经结算但账本写入失败,请求仍然返回 200。 动作已经发生,钱已经动了;此时报告失败才是错误的答案。

安全地重试

非幂等的 POST 接受 Idempotency-Key 头。重试时发送同一个键,你会拿到第一次请求的响应, 而不是第二个 agent、第二个钱包或第二笔支付。

curl -X POST $BASE/agents \
  -H "Authorization: Bearer $INCARNA_KEY" \
  -H 'Idempotency-Key: 8f14e45f-ea6a-4f7e-9c1b-1a2b3c4d5e6f' \
  -H 'Content-Type: application/json' \
  -d '{"name":"..."}'

几条值得知道的规则:

  • 记录 24 小时后过期。
  • 一个键绑定到它第一次见到的请求体。用不同的请求体复用它会得到 422 idempotency_key_reuse,而不是悄悄重放。
  • 并发在数据库层面解决,所以两个同时发起的重试不可能都执行工作。输的那个拿到 409 idempotency_in_progress

degraded 不是错误

处于 degraded 的 agent 和其他任何 agent 一样返回 200。它表示某个开通任务用完了重试 —— 可恢复,而且会自行恢复。见生命周期