The HTTP Status Codes Problem

  • 400 to denote persistent situation (error couldn't be resolved by just repeating the request);
  • 404 to denote ‘uncertainity’ cases (the request could be repeated — possibly with different outcomes);
  • 500 to denote server-side problems, with Retry-After header to indicate the desirable retry period.
  • 400 Bad Request code when some parameters are invalid or missing. This error makes absolutely no sense to clients unless specific missing or invalid field is specified — but that's exactly the thing the standard does nothing with! There are no conventional standards to specify which parameter is wrong exactly. Yes, we can, of course, invent a standard of ourselves, but that would contradict the REST idea of protocol transparency.
    NB: some purists insist, that a 400 code indicates a problem with the request itself, i.e. malformed URI, or header, or body, etc. Sometimes 422 Unprocessable Entity or 412 Precondition Failed are claimed to be the ‘right’ code for invalid parameters error. It doesn't change anything, of course.
  • 403 Forbidden when some authorization or authentication error occurs. There are several quite different Forbidden situations, which require quite different actions from the client:
    — an authorization token is missing — the user must be invited to log in;
    — the token is expired — the token refreshing procedure must be conducted;
    — the token belongs to other user — usually indicates that some caches are stall;
    — the token is revoked — usually a result of user logging out on all devices;
    — the user is bruteforcing the authorization endpoint — some antifraud action is required;
    — etc.
    Each 403 reason indicates quite different scenarios, some of them (bruteforcing) have nothing in common with others.
  • 409 Conflict;
  • tons of them.

So, what are you proposing, pal?

  • Abandon REST paradigm, stick to pure RPC. Use HTTP status codes to indicate the problems with the HTTP network layer itself. So you would actually need just 2 of them:
    200 OK if the server got the request, regardless of the result — execution errors are to be returned as 200
    500 Internal Server Error if the request can't reach the server.
    You may employ 400 Bad Request also to denote client errors; it slightly complicates the setup, but allows for using some interim software like API gateways.
  • ‘Run with scissors’, using common practices, just cautiously, avoiding violating HTTP semantics. Use HTTP status codes to separate graphs (sometimes using exotic codes). Describe errors semantically and make sure clients don’t try to detect anything valuable from the status codes.
    NB: some industrial-grade platforms manage to do both, i.e. combine a pure RPC-style approach with extensively employing various HTTP status codes to indicate a subset of problems (403s and 429s for instance, which are purely business logic-bound, having nothing to do with the HTTP itself). Though in a practical sense this approach seems to work, it’s very hard to to tell which problems they face in modern smart-proxy rich environments, not mentioning aesthetic impressions.
  • Try organizing the mess. Including, but not limited to:
    — using HTTP codes to indicate the problems expressible in HTTP terms (like using 406 Unacceptable to indicate invalid Accept-Language request header value);
    — defining additional machine-readable error response details, preferably in a form of HTTP headers (since reading them doesn’t require parsing the entire response body, so interim proxies and API gateways might operate them less expensively); for example, use something like an X-My-API-Error-Reason header containing with pre-defined semantic errors nomenclature;
    — customize graphs and monitoring to make them use this specific data in addition to the status codes or instead of them;
    — make sure that clients are treating status codes and the error details correctly, especially with regard to dealing with unknown errors.




Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Pig Latin

Creating and maintaining patterns in PatternLab

Photo of the facade of the Palacio de Cristal, of four glass big windows, showing the inside of the building.

Pass function as parameter to a function in Apex

Can DevOps Work for Small Organizations?

Configuring Docker Containers using Ansible

How to prepare for AWS Solutions Architect Associate Certification

Introduction to Kotlin Multiplatform Mobile — Getting Started

How Agile done wrong can hurt your company

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Sergey Konstantinov

Sergey Konstantinov

The API Guy

More from Medium

Intro to Event-Driven Architecture

OAuth and OIDC Part 1

Microservices did you know this ?

Proxy Design Pattern Usage