← [ BACK_TO_NOTES ]

稳定 API 错误模型

把网络错误、HTTP status、应用 code、用户文案、内部 cause 与 requestId 分层,形成可版本化、可观测且不泄露内部实现的 API 错误契约。

#synthesis #evergreen #api #architecture

[!info] related notes

稳定 API 错误模型

核心问题

客户端需要根据错误采取可预测动作,但内部异常文本会随着数据库、框架、Provider 和实现重构而变化,还可能泄露 SQL、路径、堆栈或敏感信息。

因此要把错误至少拆成:

Transport / Network Error
HTTP Status
Application Code
User-facing Message
Structured Details
Request / Trace Identifier
Internal Cause / Stack

推荐响应

{
  "error": {
    "code": "VERSION_CONFLICT",
    "message": "数据已被其他操作更新,请刷新后重试。",
    "details": {
      "expectedRevision": 12,
      "actualRevision": 13
    },
    "requestId": "req_..."
  }
}
  • status:协议级类别,例如 401、403、409、422、429、500;
  • code:稳定、可版本化的应用语义;
  • message:面向用户,可本地化,不作为程序分支;
  • details:只有客户端确实需要的结构化信息;
  • requestId:安全的报障关联标识;
  • cause/stack/provider payload:只进入受控日志和 trace。

分层规则

网络错误

DNS、connect、TLS、timeout 或连接中断可能没有 HTTP response。客户端要单独建模,不能伪造成 500。

HTTP Status

Status 表达广义语义,不承担全部业务细节:

  • 401:缺少或失效的认证;
  • 403:身份已知但无权限;
  • 409:当前资源状态、revision 或唯一约束冲突;
  • 422:可解析但业务输入/字段校验失败;
  • 429:限流;
  • 5xx:服务端未能完成请求。

团队应保持一致,不把状态码选择变成没有业务价值的教条。

Application Code

Code 应描述客户端需要区分的稳定语义:

  • AUTH_EXPIRED;
  • PERMISSION_DENIED;
  • VERSION_CONFLICT;
  • RUN_CANCELLED;
  • RUN_WAITING_FOR_APPROVAL;
  • RATE_LIMITED。

不要把每个 ORM exception 或 Provider error 暴露成公共 code。

Domain Error 与 Infrastructure Error

领域错误是预期分支,例如任务状态不允许完成;基础设施错误是 DB、网络、Provider 等实现故障。两者都可以映射到稳定 API error,但日志和告警级别不同。

Domain Error
→ 稳定 code + 可操作 message
→ 通常不需要 error alert

Infrastructure Error
→ generic public code
→ internal cause + trace + alert

Streaming / Agent 场景

HTTP headers 发出后不能再改变 status。流式协议必须有结构化 error event:

{
  "type": "run_failed",
  "runId": "run_...",
  "code": "MODEL_TIMEOUT",
  "message": "本次生成未完成。",
  "requestId": "req_..."
}

连接中断不等于 run_failed,更不等于完成。客户端应通过 durable run state 或重连协议确认最终状态。

重试信息

服务可以提供机器可读重试提示,但不能诱导危险自动重试:

{
  "code": "RATE_LIMITED",
  "retryable": true,
  "retryAfterMs": 1200
}

是否真正重试还取决于:

  • 操作是否幂等;
  • body 是否可 replay;
  • 是否已经产生副作用;
  • 当前 retry budget;
  • 是否会形成重试风暴。

安全边界

  • 不回传 stack、SQL、文件路径、Provider secret 和内部 host;
  • 404 可以在防资源枚举场景隐藏“存在但无权限”;
  • details 必须经过字段 allowlist 和权限检查;
  • 日志中的用户数据和 token 同样需要脱敏;
  • requestId 不能编码租户秘密,也不能作为授权凭据。

Contract Test

至少验证:

  • 同一领域错误在 HTTP 和 IPC adapter 中映射为同一 code;
  • 401 不会被误映射为 403;
  • 未知异常不会泄露内部 message;
  • 字段错误具有稳定 details schema;
  • Streaming terminal error 与 durable run state 一致;
  • 旧客户端遇到新 code 有 generic fallback。

面试回答

HTTP status 只表达协议层类别,客户端真正稳定分支应依赖版本化 application code;message 用于展示,不用于判断。响应还可以带结构化 details 和 requestId,内部 stack/cause 只进入日志。网络错误、领域错误和基础设施错误要分层;在 SSE 等流式场景,headers 发出后还需要稳定的 error event 和独立的 durable run terminal state。

创建于 2026/9/2 更新于 2026/9/2
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本