npx skills add ...
npx skills add samber/developer-platform-skills --skill api-error-design
Design the error surface of a public API so integrators self-serve fixes - a machine-readable error-code taxonomy (flat catalog, code/subcode, Google-style domain/reason), the RFC 9457 problem-details envelope with extension members, actionable error messages, retryability signaling (retryable flag, Retry-After), and per-endpoint error documentation with request-ID tracking. Use whenever the user mentions API error codes, an error taxonomy, RFC 9457, application/problem+json, 4xx/5xx response bodies, or confusing API error messages - even if they never say "error design". Do NOT use for client-side retry mechanics - use samber/developer-platform-skills@api-idempotency-retry - nor for incident and status-page communication - use samber/developer-platform-skills@api-status-communication instead.
npx skills add samber/developer-platform-skills --skill api-error-design
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."
Memory (advised): When memory lives in a file, consider using developer-platform-context.md; if a different memory system is in use, rely on that instead. The file is an advisory reference, not a mandatory requirement. Separate task info in different sections. Remove finished tasks. Add a date to a task; no date for general project context. Some interview responses may differ between 2 tasks.
Ask these before designing anything; each answer changes a later step. Batch them - this is a tactical design task, not a strategy interview.
The split that changes the design is consumer type:
documentation_url per coderetryable boolean and typed detail fields matter most; prose messages are secondary and must never be the only signal.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.
Each step has a section below, in order.
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 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.
type, title, status, detail, instance, media type application/problem+json.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.errors array of typed result objects instead; samber/developer-platform-skills@public-graphql-api-design owns that shape.See references/rfc9457-payload-examples.md for complete good and bad payloads.
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.
{"code": 2945, "description": "LESS_THAN_MIN_OCCURANCE"} - a rule named with no field, no expected value, no self-serve path.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.documentation_url to the code's catalog entry instead of cramming the paragraph into message.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.
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.
Retry-After), 502, 503, 504, and sometimes 500Retry-After header on 429 and 503 - a real HTTP header generic clients and proxies parse without understanding your body format.retryable, retry_after, and optional guidance fields - when a header under-specifies the caller's options.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.Payload examples live in references/rfc9457-payload-examples.md.
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.documentation_url points at. Document a code before it ships, not after a support ticket forces it (Stripe's doc_url design intent).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.See references/error-documentation-examples.md for the OpenAPI shape and request-ID wiring.
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 onmessage/detail text is not contractual and may change without notice (Google states this outright for Status.message)Evolve by addition only:
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".
Anti-pattern checklist - each is a direct audit finding:
code value.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.
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.