An API error response needs two audiences: software that must choose a recovery path and a person who must understand what happened. Use an appropriate HTTP status, a stable machine-readable problem type or code, a concise title, and safe detail when it helps the task. A request identifier can connect the response to operational logs without copying a private case body into the error. Field validation may include paths or field names, but never trust the client to interpret a human sentence as the only programmatic signal. Keep permission and existence decisions deliberate so errors do not disclose hidden records.
Structured API Errors and Recovery
Working case
Reviewer 31 tries to resolve case 47 with an old version. The API should return a failed-precondition status and a stable error code the UI recognizes as a merge conflict. The UI then offers to compare current data while preserving the unsaved note. A malformed note should instead mark the field to correct. A temporarily unavailable report worker should offer a later retry or status check. If all three failures are returned as status 200 with {success:false}, caches, monitoring, and generic clients lose the distinction.
Implementation boundary
function versionConflictProblem(requestId) {
return {
status: 412, type: "about:blank", title: "Precondition failed",
detail: "This case changed before your save.", code: "case_version_conflict", requestId
};
}
console.log(versionConflictProblem("req-47").code);
// Output: case_version_conflictSend the representation with an error media type if the API adopts the problem-details format, and keep its status member consistent with the actual HTTP status. The generic type in this sketch is enough because the stable extension code drives the application's recovery decision; a product may use its own type identifier instead. Do not put stack traces, SQL, tokens, or another user's private note in detail. Use field identifiers the UI can map to controls, and allow the human message to be localized without changing the machine code. A 404 or 403 policy for hidden records must be consistent across routes.
Cost and boundaries
A common error shape adds a little response data and shared handling code, but reduces endpoint-specific parsing and ambiguous recovery. Versioning the machine code requires care: removing or reusing a code can break older clients. Keep a small registry of codes, statuses, and intended client actions. Logging only request IDs and classified errors keeps the response safe, but support teams need a controlled path from that ID to diagnostics. Avoid recording unbounded raw validation input as an error extension.
Failure trace
A handler catches every exception and returns 200 with a free-text message. The case UI treats a failed save as success because it only checks response.ok; the reviewer leaves and loses the note. Another handler returns a detailed database error to help support, exposing internal table names and a private value. Test status and body together for conflict, validation, denial, and dependency failure. Make the UI branch on stable code and preserve its draft until the mutation is confirmed, not until any response arrives.
Verification
- Conflict, validation, denial, and dependency failures have distinct status/code pairs.
- Changing human wording does not change the client's recovery branch.
- Error bodies contain no raw private content, credentials, or internal stack traces.
Practice drill
Send four bad requests: stale version, missing required note, forbidden case, and temporarily unavailable worker. Write the expected status, machine code, safe detail, and UI recovery for each before implementing handlers. Inspect actual responses for tokens, raw note text, stack traces, and internal paths. Change the human wording and confirm the client still behaves correctly. Then simulate an old client that does not know a new code; it should fall back to a safe generic error rather than report success.
Decision note
Use status codes for transport meaning and stable machine codes for product recovery, while keeping human detail safe and replaceable.
Common Mistakes
- Returning status 200 for a failed mutation.
- Branching on a localized human sentence as the sole error code.
- Exposing internal exceptions to make debugging easier.
Connected lessons
API Mutation and Failure Contracts; Conditional Writes and Lost-Update Prevention; Request Deadlines, Retries, and Backoff; Accepted Operations and Status Resources; Routing: validate path parameters and return a stable error shape; API Evolution and Compatibility Windows; Telemetry Minimization and Retention.
Apply and check
Build Project: conflict-safe case API and review Web Development: API mutation contracts quiz.
