Failure taxonomy
Give each failure class a different safe response
The user message, HTTP semantics, retry policy and operator evidence should agree without exposing internal data.
Invalid input
The request cannot satisfy the documented contract.
- User
- Identify the field and correction
- System
- Reject before side effects
- Avoid
- Retrying the same invalid payload
Denied action
Identity is missing or permission is insufficient.
- User
- Safe sign-in or permission guidance
- System
- Deny consistently and audit sensitive attempts
- Avoid
- Leaking whether another user’s object exists
Temporary dependency
A required system is unavailable or rate limited.
- User
- Preserve work and explain retry state
- System
- Bound timeout, backoff and circuit behavior
- Avoid
- Immediate unbounded retries
Ambiguous outcome
The caller cannot tell whether a consequential action completed.
- User
- Show pending rather than false failure
- System
- Reconcile using idempotency and correlation
- Avoid
- Repeating a payment or write blindly
Unknown failure
The system violated an assumption or encountered an unclassified state.
- User
- Stable reference and safe next step
- System
- Sanitized logging and owned alert
- Avoid
- Stack traces, SQL or secrets in responses
Operating principle
Separate the public explanation from the private diagnosis
A useful user-facing message explains consequence and recovery in the product’s language. A useful operator event preserves structured technical context, correlation and ownership.
Combining the two often produces either an empty ‘something went wrong’ or a dangerous leak. Design both outputs from the same classified failure without copying sensitive internals into the response.
- Preserve user work where possible
- Use stable error categories
- Attach correlation context
- Never expose secrets or stack traces
Applied example
Failure example: catch, log and return success
A generated form handler catches a database exception, writes it to the console and returns a success message so the page does not crash.
- The user believes the action completed
- The system lost the write
- No operator-owned event exists
- Retry may now create inconsistent state
Plain answers
Questions to resolve before shipping
Should users see technical error codes?+
Use a stable reference when it helps support, but explain consequence and recovery in user language rather than exposing implementation detail.
Is returning HTTP 200 with an error object acceptable?+
Some protocols choose that convention, but ordinary HTTP APIs benefit from semantics that distinguish success and failure consistently.
How many error categories are enough?+
Start with categories that produce different user actions or operational responses; avoid hundreds of labels that nobody owns.