Errors and recovery contracts
Give clients machine readable problems, safe recovery instructions and an error vocabulary that stays stable.
On this page
Before you start
- HTTP response semantics
- Validation rules
- An operational correlation key
From a failed rule to a useful response
Translate internal failure into a stable public problem without exposing implementation details.
Scroll across the diagram to see all four systems.
Submit representation
Parse the request and establish the authenticated context.
Identify failed rule
Distinguish malformed JSON, a validation error and an operational conflict.
Record diagnostic context
Write the request reference and protected internal diagnostic details.
Return problem details
Return a stable problem type, matching HTTP status and safe field guidance.
Use a problem type as a stable contract
RFC 9457 defines application/problem+json for API problem details. The type identifies the kind of problem; title gives its human readable summary. The detail describes this occurrence, while instance can identify this particular failure. Clients should branch on a documented type or extension code, not on translated prose.
A problem body supplements HTTP status. It does not replace it. A proxy may change or remove a body, and generic clients still need the correct status to decide whether authentication, validation or retry is relevant.
{
"type": "https://api.example.test/problems/invalid-contact",
"title": "Contact validation failed",
"status": 422,
"detail": "Correct the listed fields before submitting again.",
"instance": "/requests/req_2409",
"errors": [
{
"pointer": "/email",
"code": "invalid_format",
"message": "Provide an email address with a valid format."
}
]
}Separate a failed request from a failed dependency
Use 400 when the request cannot be interpreted, 401 when valid authentication is absent, and 403 when the authenticated action is denied. Use 409 for a conflict with current resource state and 412 for a failed request precondition. Validation of understood content can use 422.
For 429 or a temporary service failure, describe the retry policy separately from the business result. A timeout after a write is an unknown outcome, not evidence that no write occurred. The client must reconcile that uncertainty before creating a second business action.
| Condition | Client action | Avoid |
|---|---|---|
| 401 | Obtain valid credentials through the configured flow. | Retrying the same expired token indefinitely. |
| 403 | Review action scope or object permission. | Automatically requesting broader permissions. |
| 409 / 412 | Read current state and resolve the conflict. | Blindly replaying a stale mutation. |
| 422 | Correct the specific representation. | Retrying the unchanged payload. |
| 429 / 503 | Respect retry guidance and the operation deadline. | Immediate synchronized retries from every worker. |
Point to the field without leaking the record
For validation errors, expose a machine readable path such as a JSON Pointer plus a stable reason code. If several fields are invalid, return the useful set in one response. Limit list size to avoid turning error handling into an unbounded response.
Avoid revealing whether a protected email, account or tenant exists. Detailed database errors, stack traces, signing material and upstream response bodies belong in restricted logs. A public request reference lets support locate diagnostics without publishing them.
Keep failure rendering simpler than the failing system
A failure response should remain available when a downstream service is unavailable. Do not require the failed dependency to build the error description. Return the safest known status and preserve the trace context for later diagnosis.
Treat problem type changes like schema changes. New optional fields can be additive; changing the meaning of an existing type can break a client even when its JSON parser still succeeds.
Test the recovery instruction as well as the status
For each public problem, test the actual next action: reauthentication, payload correction, refresh, delayed retry or manual review. Verify that the described recovery can succeed and does not duplicate work.
Include non JSON intermediary responses, empty bodies and connection loss in client tests. A robust client must fall back to the transport status and correlation context when the preferred error document is absent.
Classify a request failure
Inspect how authentication, business validation and dependency availability map to distinct responses.
/api/v1/contactsExpected outcome
Contact created
The request passes identity and representation checks before one record is created.
{
"id": "contact_241",
"email": "alex@example.test"
}Location: /api/v1/contacts/contact_241Decision path
- Authenticate
- Validate contact representation
- Persist the contact
- Return its resource location
Input schema 3 fields
| Field | Type | Meaning |
|---|---|---|
authenticatedRequired | boolean | Whether authentication succeeded. |
email_validRequired | boolean | Result of the email representation check. |
dependency_availableRequired | boolean | Whether the required write service is available. |
Standards and documentation
Explore the protocol specifications and platform guidance for this topic.
Bring the contract into your operating environment.
Discuss an implementation