Status Codes, Errors and Messages - Build APIs You Won't Hate: Everyone and their dog wants an API, so you should probably learn how to build them (2014)

Build APIs You Won't Hate: Everyone and their dog wants an API, so you should probably learn how to build them (2014)

4. Status Codes, Errors and Messages

4.1 Introduction

If everything goes smoothly you want to show some data. If a valid request comes in for a data which is valid you show data, if creating something on the API with valid data, you show the created object. If something goes wrong, however, you want to let people know what is wrong using two simultaneous approaches:

1. HTTP status codes

2. Custom error codes and messages

4.2 HTTP Status Codes

Status Codes are used in all responses and have a number from 200 to 507 - with plenty of gaps in between - and each has a message and a definition. Most server-side languages, frameworks, etc default to “200 OK”.

Status codes are grouped into a few different categories:

2xx is all about success
Whatever the client tried to do was successful up to the point that the response was send. Keep in mind that a status like 202 Accepted doesn’t say anything about the actual result, it only indicates that a request was accepted and is being processed asynchronously.

3xx is all about redirection
These are all about sending the calling application somewhere else for the actual resource. The best known of these are the 303 See Other and the 301 Moved Permanently which are used a lot on the web to redirect a browser to another URL.

4xx is all about client errors
With these status codes we indicate that the client has done something invalid and needs to fix the request before resending it.

5xx is all about service errors
With these status codes we indicate that something went wrong in the service. For example a database connection failed. Typically a client application can retry the request. The server can even specify when the client is allowed to retry the command using a Retry-After HTTP header.

Using HTTP status codes in a REST service – Maurice de Beijer

For a more complete list of HTTP status codes and their definitions the REST & WOA Wiki has an extensive list of them.

Arguments between developers will continue for the rest of time over the exact appropriate code to use in any given situation, but these are the status codes the API uses at Kapture:

· 200 - Generic everything is OK

· 201 - Created something OK

· 202 - Accepted but is being processed async (for a video means encoding, for an image means resizing, etc)

· 400 - Bad Request (should really be for invalid syntax, but some folks use for validation)

· 401 - Unauthorized (no current user and there should be)

· 403 - The current user is forbidden from accessing this data

· 404 - That URL is not a valid route, or the item resource does not exist

· 410 - Data has been deleted, deactivated, suspended, etc

· 405 - Method Not Allowed (your framework will probably do this for you)

· 500 - Something unexpected happened and it is the APIs fault

· 503 - API is not here right now, please try again later

It can be tempting to try and squeeze as many error codes in as you can, but I would advise you to try and keep it simple. You won’t unlock any achievements for using them all.

Most 5xx issues will most likely happen under odd architecture or server related issues that are nothing to do with your API, like if PHP-FPM segfaults behind nginx (502), if your Amazon Elastic Load Balancer has no healthy instances (503) or if your hard-drive fills up somehow (507).

4.3 Error Codes and Error Messages

Error codes are usually strings or integers that act as a unique index to a correspond human-readable error message with more information about what is going wrong. That sounds a lot like HTTP status codes, but these errors are about application specific things that may or may not be anything to do with HTTP specific responses.

Some folks will try to use HTTP status codes exclusively and skip using error codes because they do not like the idea of making their own error codes or having to document them, but this is not a scalable approach. There will be some situations where the same endpoint could easily return the same status code for more than one different condition. The status codes are there to merely hint what is going on, relying on the actual error code and error message to provide more information if the client is interested.

For example, an issue with the access token will always result in the user not being recognized. An uninterested client would simply say “User could not get in” while a more interested client would probably prefer to offer suggestions via messages in their own webapp/iPhone app interface.

1 {

2 "error": {

3 "type": "OAuthException",

4 "message": "Session has expired at unix time 1385243766. The current unix time is 1385\

5 848532."

6 }

7 }

Everyone can understand that.

Facebook sadly is missing an error code, so sometimes you find yourself doing string checking on the message which is lunacy.

warning

Foursquare is not a bad example of using both, but they place an emphasis on tying their errors to a status code.

https://developer.foursquare.com/overview/responses

Twitter does a great job of having HTTP status codes documented and having specific error codes for other issues too. Some are tied to HTTP status codes (which is fine) but many are not. Some are also tied to the same status code, highlighting the issues raised above.

https://dev.twitter.com/docs/error-codes-responses

Code

Text

Description

161

You are unable to follow more people at this time

Corresponds with HTTP 403 - thrown when a user cannot follow another user due to some kind of limit

179

Sorry, you are not authorized to see this status

Corresponds with HTTP 403 - thrown when a Tweet cannot be viewed by the authenticating user, usually due to the tweet’s author having protected their tweets.

Programmatically Detecting Error Codes

You can use error codes to make an application respond intelligently to failure of something as basic as a posted Twitter status.

Using Python to catch exceptions and react to the Twitter error code


1 try:

2 api.PostUpdates(body['text'])

3

4 except twitter.TwitterError, exc:

5

6 skip_codes = [

7 # Page does not exist

8 34,

9

10 # You cannot send messages to users who are not following you

11 150,

12

13 # Sent too many

14 # TODO Make this requeue with a dekay somehow

15 151

16 ]

17

18 error_code = exc.__getitem__(0)[0]['code']

19

20 # If the token has expired then lets knock it out so we don't try again

21 if error_code in skip_codes:

22 message.reject()

23

24 else:

25 # Rate limit exceeded? Might be worth taking a nap before we requeue

26 if error_code == 88:

27 time.sleep(10)

28

29 message.requeue()


Compare this sort of logic with Facebook - and their lack of error codes:

Using Python to analyise Facebook error strings as no codes exist


1 except facebook.GraphAPIError, e:

2

3 phrases = ['expired', 'session has been invalidated']

4

5 for phrase in phrases:

6

7 # If the token has expired then lets knock it out so we dont try again

8 if e.message.find(phrase) > 0:

9 log.info("Deactivating Token %s", user['token_id'])

10 self._deactivate_token(user['token_id'])

11

12 log.error("-- Unknown Facebook Error", exec_info=True)


If they change their error messages then this might stop working, which would be a problem. Codes (that do not change) are a much more sensible way to go about this.

1 {

2 "error": {

3 "type": "OAuthException",

4 "code": "ERR-01234",

5 "message": "Session has expired at unix time 1385243766. The current unix time is 1385\

6 848532."

7 "documentation_url": "http://example.com/docs/errors/#ERR-01234"

8 }

9 }

4.4 Error or Errors

When returning errors, especially for validation, it can seem like a great idea to try and return multiple errors at a time to make things quicker for whatever client is interacting with the API. This sounds good in practice, but I find it’s best to simply stop processing (exit out) after the first error to avoid further controller interaction. This means less work goes into making a maintainable state for your controller after something goes wrong, and really is how most APIs work.

Try A, get error 1. Try B, get error 2. Try C, it works!

4.5 Common Pitfalls

200 OK and Error Code

If you return a HTTP status code of 200 with an error code then Chuck Norris will roundhouse your door in, destroy your computer, instantly 35-pass wipe your backups, cancel your Dropbox account and block you from GitHub. HTTP 4xx or 5xx codes alert the client that something bad happened, and error codes provide specifics of the exact issue if the client is interested.

Non-Existent, Gone, or Hiding?

404 is drastically overused in applications. People use it for “never existed”, “no longer exists”, “you can’t view it” and “it is deactivated” which is way too vague. That can be split up into 404, 403 and 410 but this is still vague.

If you get a 403 this could be because the requesting user is in not in the correct group to see the requested content. Should the client suggest you upgrade your account somehow? Are you not friends with the users content you are trying to view? Should the client suggest you add them as a friend?

A 410 on a resource could be due to a user deleting that entire piece of content or it could be down to the user deleting their entire account.

In all of these situations the ideal solution is to complement the HTTP status code with an error code, which can be whatever you want as long as they are unique within your API and documented somewhere. Do not do what Google does and supply a list of error codes then have other error codes which are not documented anywhere, because if I see that I will come for you.