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.
- Skill ID
- samber/developer-platform-skills/api-error-design
- Publisher
- samber
- Repository
- developer-platform-skills
- Installs
- 319
- Files
- 6
- License
- MIT
- Synced
- Oct 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
samber/developer-platform-skills/api-error-designInstalls these files- SKILL.md
- evals/evals.json
- references/error-documentation-examples.md
- references/message-rewrite-examples.md
- references/rfc9457-payload-examples.md
- references/taxonomy-catalog-examples.md
What this skill tells the agent
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."
Clarifying questions
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.
- Paradigm: REST-only, gRPC-only, or both? (drives the taxonomy choice)
- Greenfield or retrofit? If retrofit, request 5-10 real production error responses across different endpoints.
- Which codes, fields, or message strings do existing clients already branch on? (those are contract - see Stability contract)
- Who consumes the errors: first-party app developers, third-party integrators, machine/agent callers, or a mix? (see next section)
- Does the domain have layered failure causes (payments-style declines, fraud, compliance) where one code per error genuinely under-informs?
- 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)
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_urlper code - field/value/constraint detail
- a request ID for support escalation
- Machine and agent consumers - branch only on stable structured fields. A
retryableboolean 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
messagetext 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.
Workflow
- Audit the existing error surface (or enumerate failure conditions, if greenfield).
- Choose the taxonomy - flat catalog, code/subcode, or domain/reason.
- Adopt the envelope - status code first, then RFC 9457 with extension members.
- Rewrite the messages - human-readable, actionable, consistent.
- Design the retryability signals.
- Document every error per endpoint and wire request-ID tracking.
Each step has a section below, in order.
1. Audit the error surface
- 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
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
codeper category, each pinned to one HTTP status, withsubcodesfor 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+reasonidentify the error independently of HTTP status, with ametadatamap 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.
3. Adopt the envelope
- 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.
- Use RFC 9457 problem details (it obsoletes RFC 7807; name 9457 as the standard):
type,title,status,detail,instance, media typeapplication/problem+json. - 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.
More from samber
Use this skill in a project
Open any RiverX project, search the Skills panel for samber/developer-platform-skills/api-error-design, and install it in one click.
