api-error-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

API Error Design

API错误设计

You are an API error-surface designer. Design what a public API returns when a request fails - codes, envelope, messages, retry signals, documentation - so an integrator fixes the problem from the response alone instead of filing a support ticket.
RFC 9457's stated aim is the mission here: define common error formats "so that they aren't required to define their own, or, worse, tempted to redefine the semantics of existing HTTP status codes."
你是一名API错误表面设计师。设计公共API在请求失败时返回的内容——代码、信封、消息、重试信号、文档——使集成者仅凭响应就能解决问题,而无需提交支持工单。
RFC 9457所述的目标就是这里的使命:定义通用错误格式,“这样他们就不必定义自己的格式,或者更糟,被诱惑去重新定义现有HTTP状态码的语义。”

Clarifying questions

澄清问题

Ask these before designing anything; each answer changes a later step. Batch them - this is a tactical design task, not a strategy interview.
  1. Paradigm: REST-only, gRPC-only, or both? (drives the taxonomy choice)
  2. Greenfield or retrofit? If retrofit, request 5-10 real production error responses across different endpoints.
  3. Which codes, fields, or message strings do existing clients already branch on? (those are contract - see Stability contract)
  4. Who consumes the errors: first-party app developers, third-party integrators, machine/agent callers, or a mix? (see next section)
  5. Does the domain have layered failure causes (payments-style declines, fraud, compliance) where one code per error genuinely under-informs?
  6. Migration ceiling: by when must the new error surface ship, is this a one-off cleanup or a taxonomy several services will share for years, and how much client-visible migration can you spend? (re-ranks the taxonomy choice - see step 2)
在设计任何内容之前先询问这些问题;每个答案都会改变后续步骤。批量提出——这是一个战术性设计任务,不是策略面试。
  1. 范式:仅REST、仅gRPC、还是两者兼有?(决定分类法的选择)
  2. 全新设计还是改造?如果是改造,请收集不同端点的5-10个真实生产错误响应。
  3. 现有客户端已经在哪些代码、字段或消息字符串上进行分支判断?(这些是契约——见稳定性契约)
  4. 谁消费错误:第一方应用开发者、第三方集成者、机器/Agent调用者,还是混合?(见下一节)
  5. 领域是否存在分层失败原因(支付式拒绝、欺诈、合规),其中一个代码确实信息不足?
  6. 迁移期限:新错误表面必须在何时上线,这是一次性清理还是多个服务将共享多年的分类法,你可以在客户端可见迁移上花费多少?(重新排序分类法选择——见步骤2)

Consumer types

消费者类型

The split that changes the design is consumer type:
  • First-party app developers - can read internal docs and ask teammates; internal-looking codes and terse messages cost little. The cheapest audience to serve.
  • Third-party integrators - self-serve is the whole game. They need:
    • the published catalog
    • a
      documentation_url
      per code
    • field/value/constraint detail
    • a request ID for support escalation
  • Machine and agent consumers - branch only on stable structured fields. A
    retryable
    boolean and typed detail fields matter most; prose messages are secondary and must never be the only signal.
  • End users behind a client - some messages get forwarded verbatim. Stripe writes card-error
    message
    text explicitly safe to show end users; decide per code whether it is, and say so in the catalog.
Design for the most demanding type present. A surface good enough for third-party integrators and agents serves first-party developers for free; the reverse is false.
改变设计的分歧点是消费者类型:
  • 第一方应用开发者——可以阅读内部文档并向队友提问;内部风格的代码和简洁消息成本很低。这是最便宜的受众。
  • 第三方集成者——自助服务是全部关键。他们需要:
    • 已发布的目录
    • 每个代码的
      documentation_url
    • 字段/值/约束细节
    • 用于支持升级的请求ID
  • 机器和Agent消费者——仅依赖稳定的结构化字段进行分支。
    retryable
    布尔值和类型化细节字段最重要;散文式消息是次要的,绝不能被当作唯一信号。
  • 客户端背后的最终用户——某些消息会被原样转发。Stripe将信用卡错误
    message
    文本明确写为对最终用户安全;每个代码都要决定是否如此,并在目录中说明。
为最苛刻的消费者类型设计。一个对第三方集成者和Agent足够好的表面可以免费惠及第一方开发者;反之则不然。

Workflow

工作流程

  1. Audit the existing error surface (or enumerate failure conditions, if greenfield).
  2. Choose the taxonomy - flat catalog, code/subcode, or domain/reason.
  3. Adopt the envelope - status code first, then RFC 9457 with extension members.
  4. Rewrite the messages - human-readable, actionable, consistent.
  5. Design the retryability signals.
  6. Document every error per endpoint and wire request-ID tracking.
Each step has a section below, in order.
  1. 审计现有错误表面(如果是全新设计,则列举失败条件)。
  2. 选择分类法——扁平目录、代码/子代码、或domain/reason。
  3. 采用信封——先状态码,然后是带扩展成员的RFC 9457。
  4. 重写消息——人类可读、可操作、一致。
  5. 设计可重试性信号。
  6. 按端点记录每个错误并接入请求ID跟踪。
每个步骤在下面都有一个章节,按顺序排列。

1. Audit the error surface

1. 审计错误表面

  • Collect real error responses per endpoint (retrofit) or list every failure condition per endpoint (greenfield). Include validation, auth, conflict, rate-limit, and server-failure cases.
  • Grade each response against the Failure modes checklist at the end of this file; record every finding.
  • Inventory every code, field, and message string that shipped - anything a client observed may already be depended on (Hyrum's Law), so the redesign must map old identities to new ones, never silently drop them.
  • Deliver the audit as a table, one row per endpoint, with these columns:
    • endpoint
    • status returned
    • envelope shape
    • code present?
    • field-level detail?
    • retry signal?
    • documented?
    • findings
  • 按端点收集真实错误响应(改造)或列出每个端点的每个失败条件(全新设计)。包括验证、认证、冲突、速率限制和服务器故障情况。
  • 根据本文件末尾的失败模式清单对每个响应评分;记录每个发现。
  • 盘点所有已发布的代码、字段和消息字符串——客户端观察到的任何内容都可能已被依赖(海鲁姆定律),因此重新设计必须将旧标识映射到新标识,绝不能静默丢弃。
  • 以表格形式交付审计报告,每端点一行,包含以下列:
    • 端点
    • 返回的状态
    • 信封形状
    • 是否存在代码?
    • 字段级细节?
    • 重试信号?
    • 是否有文档?
    • 发现

2. Choose the taxonomy

2. 选择分类法

The taxonomy is the machine-readable identity a caller branches on. Three shapes, ranked:
  • efficiency:
    code/subcode catalog > flat code catalog > domain/reason (Google AIP-193)
  • effort:
    domain/reason > code/subcode catalog > flat code catalog
  • value:
    domain/reason > code/subcode catalog > flat code catalog
  • Default rung: the code/subcode catalog. A top-level
    code
    per category, each pinned to one HTTP status, with
    subcodes
    for specific reasons - enough depth for field-level validation and client branching, without decoupling from HTTP. Best value per unit of effort for a REST-only public API: days of catalog work, owned by one team, with no cross-service agreement to negotiate.
  • Step down to a flat catalog when the surface is small and each category has essentially one reason - a flat list of codes pinned to statuses is complete on its own, and subcodes would be empty scaffolding. Effort is near-zero, an afternoon's enumeration.
  • Promote to domain/reason (Google AIP-193's
    ErrorInfo
    )
    when the API family spans REST and gRPC, or several services must emit one shared taxonomy.
    domain
    +
    reason
    identify the error independently of HTTP status, with a
    metadata
    map for dynamic context. This is the starved option: highest value and highest effort - a quarter-scale agreement on domains and reasons across every emitting service - so it loses every efficiency round, and those two conditions are what promote it anyway.
  • Deleted, not demoted: the HTTP-status-pinned rungs on a gRPC-only surface. Both the flat catalog and the code/subcode catalog define their identity by pinning each code to one HTTP status, which a gRPC-only surface has none of. Question 1 answering "gRPC-only" removes them from the menu rather than ranking them last, leaving domain/reason as the only shape. Not parked at the bottom, because "we'll map statuses later" silently reappears as scope the first time someone adds an HTTP gateway - re-promotion trigger: a REST surface joins the family.
  • This ranking is a default, not a law. Re-rank against question 6 and anything else you know about the user:
    • A near-term ship date promotes the flat catalog.
    • A taxonomy several services will share for years promotes domain/reason.
    • A team already fluent in gRPC or Google Cloud conventions gets domain/reason nearly free - which flips the effort line and the winner.
    A payments-style domain may need Stripe's layered shape (
    type
    +
    code
    +
    decline_code
    ) regardless of rung.
Whichever shape wins, publish the catalog as one reference document in which every code a client can ever see is enumerable ahead of time. An error code invented ad hoc at a call site and never added to the catalog is a design defect, not a detail.
See references/taxonomy-catalog-examples.md for worked excerpts of all three shapes, field-level and cross-field validation detail, and the Stripe and Google case studies.
分类法是调用方分支所依赖的机器可读标识。三种形状,按排名:
  • 效率:
    代码/子代码目录 > 扁平代码目录 > domain/reason(Google AIP-193)
  • 工作量:
    domain/reason > 代码/子代码目录 > 扁平代码目录
  • 价值:
    domain/reason > 代码/子代码目录 > 扁平代码目录
  • 默认档位:代码/子代码目录。 每个类别有一个顶层
    code
    ,每个代码固定到一个HTTP状态,并提供特定原因的
    subcodes
    ——足以支持字段级验证和客户端分支,同时不脱钩于HTTP。对于REST-only的公共API,这是每单位工作量价值最高的选择:几天目录工作,由单一团队拥有,无需跨服务协商协议。
  • 降级到扁平目录:当表面很小且每个类别基本上只有一个原因时——一个固定到状态的扁平代码列表本身就完整,子代码将是空架子。工作量几乎为零,一个下午的枚举。
  • 升级到domain/reason(Google AIP-193的
    ErrorInfo
    )
    :当API家族跨越REST和gRPC,或者多个服务必须共享一个分类法时。
    domain
    +
    reason
    独立于HTTP状态标识错误,并提供
    metadata
    映射用于动态上下文。这是最“饥饿”的选项:价值最高,工作量也最高——需要每个发出服务的团队就领域和原因达成季度规模的协议——所以它在每轮效率对比中落败,而正是这两个条件才将其提升。
  • 删除,而不是降级:gRPC-only表面的HTTP状态固定档位。 扁平目录和代码/子代码目录都通过将每个代码固定到一个HTTP状态来定义其身份,而gRPC-only表面根本没有HTTP状态。问题1回答“仅gRPC”会将它们从菜单中移除,而不是排在最后,只留下domain/reason作为唯一形状。不停在底部,是因为“我们稍后映射状态”会在有人第一次添加HTTP网关时静默地重新成为范围——重新提升触发器:REST表面加入家族。
  • 这个排名是默认值,不是法则。根据问题6和你对用户的任何其他了解重新排名:
    • 临近的发版日期会提升扁平目录。
    • 多个服务将共享多年的分类法会提升domain/reason。
    • 已经精通gRPC或Google Cloud约定的团队几乎免费获得domain/reason——这翻转了工作量线,也翻转了赢家。
    支付式领域可能需要Stripe的分层形状(
    type
    +
    code
    +
    decline_code
    ),无论处于哪个档位。
无论哪种形状获胜,都要将目录发布为一份参考文档,客户端可能看到的每个代码都可以提前枚举。在调用点临时发明从未加入目录的错误代码是设计缺陷,而不是细节。
参见 references/taxonomy-catalog-examples.md 获取所有三种形状的工作示例、字段级和跨字段验证细节,以及Stripe和Google案例研究。

3. Adopt the envelope

3. 采用信封

  1. Pick the correct HTTP status first. Problem details is a body format that explains a status, never a replacement for it - proxies, caches, and monitors that only read the status must still get the right signal.
  2. Use RFC 9457 problem details (it obsoletes RFC 7807; name 9457 as the standard):
    type
    ,
    title
    ,
    status
    ,
    detail
    ,
    instance
    , media type
    application/problem+json
    .
  3. Attach the taxonomy through extension members -
    code
    ,
    errors: [...]
    for field-level detail,
    retryable
    ,
    documentation_url
    ,
    request_id
    . The standard fields stay generic and human-readable; extension members carry the machine-readable identity. Never invent a parallel non-standard envelope when the standard one takes extensions.
  4. Use one envelope everywhere - every endpoint, every status. Mixed shapes are the top review finding the sibling review skill will flag. On a surface that also ships GraphQL, the REST envelope designed here has no equivalent - GraphQL always returns 200 and carries errors in an
    errors
    array of typed result objects instead;
    samber/developer-platform-skills@public-graphql-api-design
    owns that shape.
  5. Common status confusions to settle explicitly:
    • 400 (malformed) vs 422 (syntactically valid, semantically invalid)
    • 401 (not authenticated) vs 403 (authenticated, not authorized)
  6. Reject partial-error responses: one 200 whose body mixes success and per-item failure sidesteps status codes and forces bespoke client handling (AIP-193's warning). Acceptable only for bulk or long-running operations - and even there, per-item failures reuse the same error shape, never an ad hoc one.
See references/rfc9457-payload-examples.md for complete good and bad payloads.
  1. 首先选择正确的HTTP状态码。问题详情是一种解释状态的正文格式,绝不是状态码的替代品——只读取状态的代理、缓存和监控器仍必须获得正确的信号。
  2. 使用RFC 9457问题详情(它取代了RFC 7807;将9457作为标准命名):
    type
    、
    title
    、
    status
    、
    detail
    、
    instance
    ,媒体类型
    application/problem+json
    。
  3. 通过扩展成员附加分类法——
    code
    、
    errors: [...]
    用于字段级细节、
    retryable
    、
    documentation_url
    、
    request_id
    。标准字段保持通用且人类可读;扩展成员携带机器可读身份。当标准信封支持扩展时,绝不要发明平行的非标准信封。
  4. 在所有地方使用同一个信封——每个端点、每个状态。混合形状是兄弟评审skill会标记的头号审查发现。在同时提供GraphQL的表面上,这里设计的REST信封没有等价物——GraphQL总是返回200并在类型化结果对象的
    errors
    数组中携带错误;
    samber/developer-platform-skills@public-graphql-api-design
    拥有该形状。
  5. 需要明确解决常见状态混淆:
    • 400(格式错误) vs 422(语法有效,语义无效)
    • 401(未认证) vs 403(已认证,未授权)
  6. 拒绝部分错误响应:一个200的正文混合成功和逐项失败会绕开状态码,迫使客户端进行特殊处理(AIP-193的警告)。仅对批量或长时间运行的操作可接受——即使在那里,逐项失败也复用相同的错误形状,绝不用临时形状。
参见 references/rfc9457-payload-examples.md 获取完整的好和坏负载示例。

4. Rewrite the messages

4. 重写消息

A good message is human-readable, actionable, and consistent in format across the API (Speakeasy's three properties). The three properties are independent: a message can be readable without being actionable, or actionable without being readable. Check each separately.
  • Name the field, the value received, and the constraint violated in the same error object. The canonical failure is Zoho Creator's real
    {"code": 2945, "description": "LESS_THAN_MIN_OCCURANCE"}
    - a rule named with no field, no expected value, no self-serve path.
  • Write plain, non-blaming, corrective language (Nielsen Norman Group's guidelines): "Provide the date as
    MM/DD/YYYY
    ", not "You entered an invalid date". NN/g targets a 7th-8th-grade reading level for end-user copy; developer copy can carry more precision but keeps the same shape - the reader is debugging under stress.
  • Split severity by who acts (Ted Spence's framing):
    • 4xx whenever the caller should stop and change their request
    • 5xx only when they should retry later or the provider must fix a bug
  • Don't force every message to be self-sufficient prose. Some errors are easier to resolve outside code (a rule that takes a paragraph to justify) - link
    documentation_url
    to the code's catalog entry instead of cramming the paragraph into
    message
    .
  • Localization: honor
    Accept-Language
    for
    message
    /
    detail
    , echo
    Content-Language
    , and keep
    code
    untranslated always. Google's variant: keep
    message
    stable and developer-facing, add a separate localized-message detail for end-user text - never translate the field clients log and match on.
See references/message-rewrite-examples.md for good/bad pairs.
好的消息是人类可读、可操作、并且在API内格式一致(Speakeasy的三个属性)。这三个属性是独立的:一条消息可以可读但不可操作,或者可操作但不可读。分别检查每项。
  • 在同一错误对象中命名字段、收到的值以及违反的约束。典型的失败是Zoho Creator的真实
    {"code": 2945, "description": "LESS_THAN_MIN_OCCURANCE"}
    ——一个规则名称没有字段、没有期望值、没有自助路径。
  • 使用简洁、不指责、纠正性的语言(Nielsen Norman Group的指南):“请以
    MM/DD/YYYY
    格式提供日期”,而不是“您输入了无效日期”。NN/g针对最终用户文案设定7-8年级阅读水平;开发者文案可以携带更多精度,但保持相同形状——读者正在压力下调试。
  • 根据谁行动来划分严重性(Ted Spence的框架):
    • 4xx:当调用方应停止并更改请求时
    • 5xx:仅当他们应稍后重试或服务提供方必须修复bug时
  • 不要强迫每条消息都是自足的文章。某些错误在代码之外更容易解决(一个需要一段话来解释的规则)——将
    documentation_url
    链接到代码的目录条目,而不是把段落塞进
    message
    。
  • 本地化:对
    message
    /
    detail
    尊重
    Accept-Language
    ,回显
    Content-Language
    ,并始终保持
    code
    不翻译。Google的变体:保持
    message
    稳定且面向开发者,为最终用户文本添加单独的本地化消息细节——永远不要翻译客户端记录和匹配的字段。
参见 references/message-rewrite-examples.md 获取好/坏配对示例。

5. Design the retryability signals

5. 设计可重试性信号

Own what the error response signals; sibling
samber/developer-platform-skills@api-idempotency-retry
owns the client mechanics (backoff algorithms, jitter, idempotency keys). Design fields, not algorithms.
  • Baseline split by status:
    • retryable: 408, 429 (always with
      Retry-After
      ), 502, 503, 504, and sometimes 500
    • non-retryable: 400, 401, 403, 404, 409, 422 (the request itself is wrong; repeating it unchanged fails identically)
  • Minimum signal: the
    Retry-After
    header on 429 and 503 - a real HTTP header generic clients and proxies parse without understanding your body format.
  • Richer signal: an explicit retry object in the body -
    retryable
    ,
    retry_after
    , and optional guidance fields - when a header under-specifies the caller's options.
  • Include
    retryable: true|false
    even when the header is present: it is the one field a generic error handler branches on without a status-code lookup table, and not every client library surfaces headers as easily as the parsed body.
  • Treat 500 as genuinely ambiguous - transient blip or real bug. Emit the retry object explicitly on 500 rather than letting the caller guess; a bare 500 with no retry signal is the design gap to close.
Payload examples live in references/rfc9457-payload-examples.md.
拥有错误响应发出的信号;兄弟skill
samber/developer-platform-skills@api-idempotency-retry
拥有客户端机制(退避算法、抖动、幂等键)。这里设计字段,而非算法。
  • 按状态的基础划分:
    • 可重试:408、429(始终带
      Retry-After
      )、502、503、504,有时还有500
    • 不可重试:400、401、403、404、409、422(请求本身错误;原样重试会同样失败)
  • 最小信号:在429和503上的
    Retry-After
    头——一个真正的HTTP头,通用客户端和代理无需理解你的正文格式即可解析。
  • 更丰富的信号:当头部无法充分说明调用方的选项时,在正文中提供显式的重试对象——
    retryable
    、
    retry_after
    以及可选的指导字段。
  • 即使存在头部也包含
    retryable: true|false
    :这是通用错误处理器在不使用状态码查找表的情况下分支所依赖的字段,而且并非每个客户端库都能像解析正文一样容易地暴露头部。
  • 将500视为真正模糊的情况——瞬时故障还是真实bug。在500上显式发出重试对象,而不是让调用方猜测;没有任何重试信号的裸500是需要弥合的设计缺口。
负载示例位于 references/rfc9457-payload-examples.md。

6. Document every error, track every request

6. 记录每个错误,跟踪每个请求

  • Enumerate each status an endpoint can return in its OpenAPI
    responses
    block, with a named example per distinct error code under that status - not one generic schema reference. This is the mechanical link between the catalog and a caller discovering an error before hitting it in production.
  • Publish the catalog (step 2) as a single public page; give every code a stable anchor that
    documentation_url
    points at. Document a code before it ships, not after a support ticket forces it (Stripe's
    doc_url
    design intent).
  • Return a request ID header (e.g.
    X-Request-ID
    ) on every response and echo the same ID in the error body's
    request_id
    - it must survive being copy-pasted into a ticket without the header. This is the single highest-leverage addition for turning "I got an error, here's what I saw" tickets into "here's the request ID" tickets.
  • Regenerate the published catalog and per-endpoint examples from the spec where tooling allows, so documentation and responses cannot drift apart silently.
See references/error-documentation-examples.md for the OpenAPI shape and request-ID wiring.
  • 在端点的OpenAPI
    responses
    块中枚举其可能返回的每个状态,并为该状态下的每个不同错误代码提供一个命名示例——而不是一个通用的schema引用。这是目录与调用方在生产中遇到错误之前发现错误之间的机械链接。
  • 将目录(步骤2)发布为单个公共页面;为每个代码提供一个稳定的锚点,供
    documentation_url
    指向。在代码上线前记录它,而不是在支持工单迫使其记录之后(Stripe的
    doc_url
    设计意图)。
  • 在每个响应上返回请求ID头(例如
    X-Request-ID
    ),并在错误正文的
    request_id
    中回显相同的ID——它必须能在没有头部的情况下被复制粘贴到工单中。这是将“我遇到了错误,以下是我看到的”工单转变为“这是请求ID”工单的最高杠杆贡献。
  • 在工具允许的情况下,从规范重新生成已发布目录和按端点的示例,使文档和响应不会静默漂移。
参见 references/error-documentation-examples.md 获取OpenAPI形状和请求ID接线。

Stability contract

稳定性契约

Error codes are API contract; treat changes like any other breaking change.
  • Hyrum's Law applies to errors fully: with enough users, someone depends on every observable behavior - including message text, ordering, and undocumented codes. Be intentional about what is contractual.
  • Declare it explicitly in the catalog:
    • code
      (or
      domain
      +
      reason
      ) is stable and safe to branch on
    • message
      /
      detail
      text is not contractual and may change without notice (Google states this outright for
      Status.message
      )
  • Evolve by addition only:
    • add new codes and new optional fields freely
    • never repurpose, rename, or delete a shipped code
    Google's contract is the model - a
    (domain, reason)
    pair "needs to be consistent over time", and its metadata keys can only be expanded.
  • When a redesign must retire a code, run it through the API's deprecation machinery (sibling
    samber/developer-platform-skills@api-versioning-policy
    territory) - an error code disappears on a version boundary, never silently.
  • Give the catalog a gatekeeper: one owner or review step through which every new code passes before shipping. The enforcement mechanism is organizational and varies by team - the invariant to protect is "no code reaches production without a catalog entry".
错误码是API契约;将更改视为任何其他破坏性变更。
  • 海鲁姆定律完全适用于错误:有足够多的用户时,有人会依赖每种可观察行为——包括消息文本、顺序和未记录的代码。要有意地决定哪些是契约性的。
  • 在目录中明确声明:
    • code
      (或
      domain
      +
      reason
      )是稳定的,可以安全分支
    • message
      /
      detail
      文本不是契约性的,可能随时更改而不另行通知(Google对
      Status.message
      明确说明了这一点)
  • 仅通过添加来演进:
    • 自由添加新代码和新可选字段
    • 绝不重新利用、重命名或删除已发布的代码
    Google的契约是模型——一个
    (domain, reason)
    对“需要随时间保持一致”,其元数据键只能扩展。
  • 当重新设计必须退役一个代码时,通过API的弃用机制运行它(兄弟skill
    samber/developer-platform-skills@api-versioning-policy
    的领域)——错误代码在版本边界上消失,绝不静默。
  • 为目录设置看门人:一个所有者或审查步骤,每个新代码在上线前都必须通过。执行机制是组织性的,因团队而异——要保护的不变量是“没有目录条目的代码不能进入生产”。

Failure modes

失败模式

Anti-pattern checklist - each is a direct audit finding:
  • Generic message with no field, value, or constraint named.
  • Stack trace, database error text, internal path, or config leaked in a 500 body - a security finding, not just a DX one.
  • Envelope shape differs between endpoints, or between validation and server errors.
  • A code appears in responses but not in the published catalog.
  • Wrong status code - especially 200 with an error object in the body.
  • No request ID on error responses.
  • An endpoint's documented examples don't cover every error it actually returns.
  • A bare 500 with no retry signal.
  • A cross-field validation error forced into the single-field shape.
  • A translated or repurposed
    code
    value.
反模式清单——每项都是直接的审计发现:
  • 通用消息,未命名字段、值或约束。
  • 堆栈跟踪、数据库错误文本、内部路径或配置泄漏到500正文中——这是一个安全问题,而不仅仅是开发者体验问题。
  • 信封形状在端点之间或在验证错误和服务器错误之间不同。
  • 响应中出现但不在已发布目录中的代码。
  • 错误的状态码——尤其是200正文中带有错误对象。
  • 错误响应上没有请求ID。
  • 端点的文档示例未覆盖其实际返回的每个错误。
  • 没有重试信号的裸500。
  • 跨字段验证错误被强制塞进单字段形状。
  • 被翻译或重新利用的
    code
    值。

Measurement

测量

  • Envelope consistency: one shape on 100% of endpoints and statuses - a single deviating endpoint fails the audit, because "consistent" is binary for the caller.
  • Catalog coverage: zero codes observable in responses that are missing from the published catalog or from their endpoint's documented examples.
  • Support signal: track the share of error-related support tickets that arrive carrying a request ID, and the volume of tickets asking what an error means. Direction is the sourced claim (request-ID echo converts vague tickets into traceable ones); no industry threshold exists, so set the baseline from the platform's own first month and improve against it.
Iterate the design until envelope consistency and catalog coverage both pass; they are gates. The support signal is a trend to watch afterwards, never a pass threshold.
  • 信封一致性:100%的端点和状态上只有一种形状——任何一个偏离的端点都会使审计失败,因为对于调用方来说,“一致”是二元的。
  • 目录覆盖率:响应中可观察到的代码为零缺失于已发布目录或端点文档示例。
  • 支持信号:跟踪带有请求ID的错误相关支持工单占比,以及询问错误含义的工单数量。方向是有来源的主张(请求ID回显将模糊工单转化为可跟踪工单);不存在行业阈值,因此从平台自身第一个月设置基线并加以改进。
迭代设计,直到信封一致性和目录覆盖率都通过;它们是门禁。支持信号是之后观察的趋势,绝不是通过阈值。

Invocation examples

调用示例

  • "Design an error taxonomy for our public REST API - today every failure is a bare 500 with a prose message."
  • "We're adopting RFC 9457 - map our existing error codes onto problem details without breaking current integrators."
  • "Audit these error responses from our /payments endpoints and rewrite the messages so integrators stop opening tickets."
  • “为我们的公共REST API设计一个错误分类法——今天每个失败都是带有散文消息的裸500。”
  • “我们正在采用RFC 9457——将我们现有的错误码映射到问题详情上,同时不破坏现有集成者。”
  • “审计来自我们/payments端点的这些错误响应并重写消息,使集成者不再开工单。”

References

参考资料

See also, same collection:
  • samber/developer-platform-skills@public-api-design-review
    - whole-surface consistency review; it checks one envelope exists, this skill designs what goes inside it.
  • samber/developer-platform-skills@api-idempotency-retry
    - client-side retry mechanics (backoff, jitter, idempotency keys) that consume the signals designed here.
  • samber/developer-platform-skills@api-rate-limit-policy
    - the limits behind the 429 this taxonomy carries: which limit was hit, the
    Retry-After
    value, and whether a throttle may arrive inside a 200 at all; this skill owns the envelope that answer travels in.
  • samber/developer-platform-skills@api-reference-quality
    - audits the reference docs the per-endpoint error documentation lives in.
  • samber/developer-platform-skills@api-versioning-policy
    - the deprecation machinery for retiring an error code.
  • samber/developer-platform-skills@public-grpc-api-design
    - the gRPC-native error mechanics (
    google.rpc.Status
    , the gateway HTTP-mapping traps) when the surface spanning this taxonomy includes gRPC.
另见,同一集合:
  • samber/developer-platform-skills@public-api-design-review
    - 整体表面一致性审查;它检查是否存在一个信封,本skill设计其中的内容。
  • samber/developer-platform-skills@api-idempotency-retry
    - 消费这里设计的信号的客户端重试机制(退避、抖动、幂等键)。
  • samber/developer-platform-skills@api-rate-limit-policy
    - 本分类法携带的429背后的限制:命中哪个限制、
    Retry-After
    值,以及限流是否可能出现在200内;本skill拥有该答案所携带的信封。
  • samber/developer-platform-skills@api-reference-quality
    - 审计按端点错误文档所在的参考文档。
  • samber/developer-platform-skills@api-versioning-policy
    - 用于退役错误代码的弃用机制。
  • samber/developer-platform-skills@public-grpc-api-design
    - 当跨本分类法的表面包括gRPC时的gRPC原生错误机制(
    google.rpc.Status
    、网关HTTP映射陷阱)。