RESTful Error Handling

by Ethan Cerami

REST, or Representational State Transfer represents an architectural style for building distributed applications. When applied to the world of web services, REST is most commonly used to refer to the transmission of XML over HTTP, and the identification of XML resources via URIs. According to REST, HTTP, XML and URIs provide all the infrastructure for building robust web services, and most developers can therefore safely skip over the pain of learning SOAP and WSDL. If you are new to REST, check out Paul Prescod's excellent REST articles on xml.com.


A major element of web services is planning for when things go wrong, and propagating error messages back to client applications. However, unlike SOAP, REST-based web services do not have a well-defined convention for returning error messages. In fact, after surveying a number of REST-based web services in the wild, there appear to be four different alternatives for handling errors. Below, I outline the four alternatives, and then provide my opinion on which option or combination of options is best.


Option 1: Stick to HTTP Error Codes


In this scenario, the web service propagates error messages via standard HTTP Error Codes. For example, assume we have the following URL:



http://www.example.com/xml/book?id=1234&dev_token=ABCD1234

This service expects a single parameter: id indicating a book ID. The service extracts the id parameter, does a database look-up and returns an XML representation of the specified book. If the URL specifies an invalid or obsolete id parameter, the service returns an HTTP 404 Not Found Error Code.


Option 2: Return an Empty Set


In this scenario, the web service always returns back an XML document which can have 0 or more subelements. If some error occurs, an XML document with zero elements is returned. The O'Reilly Meerkat news service currently uses this approach. For example, the following URL connects to Meerkat and requests all Linux related articles from the past two days, and formats the results in RSS 0.91:



http://www.oreillynet.com/meerkat/?c=cat10&t=2DAY&_fl=xml

Now, try specifying an invalid category. For example, set c =ten, like this:



http://www.oreillynet.com/meerkat/?c=ten&t=2DAY&_fl=rss


In this case, Meerkat returns an RSS document with zero item elements. This indicates that there are no matching results, but it does not indicate whether this is a valid category ID which contains no news items, or an invalid category ID.


Option 3: Use Extended HTTP Headers


In this scenario, the web service always returns an HTTP 200 OK Status Code, but specifies an application specific error code within a separate HTTP Header. The Distributed Annotation System (DAS) currently uses this alternative approach. For example, the following URL requests sequence data from Human Chromosome 1 from the Ensembl DAS Server:



http://servlet.sanger.ac.uk:8080/das/ensembl1533/sequence?segment=1:100000,100100

Now, try issuing this request for Human Chromosome 30 (there is no human chromosome 30!):



http://servlet.sanger.ac.uk:8080/das/ensembl1533/sequence?segment=30:100000,100100


If you click on the link above, you will see an empty page. However, if you have network sniffer, you can see the following HTTP response:



HTTP/1.1 200 OK
Server: Resin/2.0.5
Content-Encoding: gzip
X-DAS-Version: 1.5
X-DAS-Server: DazzleServer/0.98 (20030508; BioJava 1.3)
X-DAS-Capabilities: dsn/1.0; dna/1.0; types/1.0;
stylesheet/1.0; features/1.0; encoding-dasgff/1.0;
encoding-xff/1.0; entry_points/1.0; error-segment/1.0;
unknown-segment/1.0; component/1.0; sequence/1.0
X-DAS-Status: 403
Content-Type: text/plain
Content-Length: 10
Date: Sun, 30 Nov 2003 21:02:13 GMT


As you can see, the DAS server has returned an HTTP 200 OK Status code and a required X-DAS-Status code. In this case, the code 403 refers to a DAS Specific error code: "Bad reference object (reference sequence unknown)".


Option 4: Return an XML Error Document


In this scenario, the web service always returns an HTTP Status Code of 200, but also includes an XML document containing an application specific error message. The XooMLe application currently uses this approach (XooMLe provides a RESTful API wrapper to the existing SOAP based Google API). For example, the Google API requires that you specify a valid developer token. If you specify an invalid token, XooMLe returns an XML error document. As the XooMLe documentation puts it, "If you do something wrong, XooMLe will tell you in a nice, tasty little XML-package." For example, try this URL:



http://xoomle.dentedreality.com.au/search/?hl=en&ie=ISO-8859-1&key=&q=oreilly+php

The request does not include a Google developer token. Hence, XooMLe returns back the following XML document:

<?xml version="1.0" ?>
<xoomleError>
<method>doGoogleSearch</method>
<errorString>Invalid Google API key supplied</errorString>
<arguments>
<hl>en</hl>
<ie>ISO-8859-1</ie>
<key></key>
<q>oreilly php</q>
</arguments>
</xoomleError>

The Amazon.com web services API also follows this approach, and each returned XML document can specify an ErrorMsg element. For example, see the Amazon.com Dev-Lite Schema.


Best Practices for REST Error Handling


Assuming you are busy implementing a REST-based web service, which error handling option do you choose? I don't believe there are (yet) any best practices for REST error handling (for an overview of other best REST practices, see Paul Costello's REST presentation, in particular [Side 59].)


Nonetheless, here are my votes for most important criteria:



  • Human Readable Error Messages: Part of the major appeal of REST based web services is that you can open any browser, type in the right URL, and see an immediate response -- no special tools needed. However, HTTP error codes do not always provide enough information. For example, if we take option 1 above, and request and invalid book ID, we get back a 404 Error Code. From the developer perspective, have we actually typed in the wrong host name, or an invalid book ID? It's not immediately clear. In Option 3 (DAS), we get back a blank page with no information. To view the actual error code, you need to run a network sniffer, or point your browser through a proxy. For all these reasons, I think Option 4 has a lot to offer. It significantly lowers the barrier for new developers, and enables all information related to a web service to be directly viewable within a web browser.


  • Application Specific Errors: Option 1 has the disadvantage of not being directly viewable within a browser. It also has the additional disadvantage of mapping all HTTP error codes to application specific error codes. HTTP status codes are specific to document retrieval and posting, and these may not map directly to your application domain. For example, one of the DAS error codes relates to invalid genomic coordinates (sequence coordinate is out of bounds/invalid). What HTTP error code would we map to in this case?


  • Machine Readable Error Codes: As a third criteria, error codes should be easily readable by other applications. For example, the XooMLe application returns back only human readable error messages, e.g. "Invalid Google API key supplied". An application parsing a XooMLe response would have to search for this specific error message, and this can be notoriously brittle -- for example, the XooMLe server might simply change the message to "Invalid Key Supplied". Error codes, such as those provided by DAS are important for programmatic control, and easy creation of exceptions. For example, if XooMLe returned a 1001 error code, a client application could do a quick lookup and immediately throw an InvalidKeyException.

Based on these three criteria, here's my vote for best error handling option:

  • Use HTTP Status Codes for problems specifically related to HTTP, and not specifically related to your web service.
  • When an error occurs, always return an XML document detailing the error.
  • Make sure the XML error document contains both an error code, and a human readable error message. For example:



    <?xml version="1.0" encoding="UTF-8" ?>
    <error>
    <error_code>1001</error_code>
    <error_msg>Invalid Google API key supplied</error_msg>
    </error>



By following these three simple practices, you can make it significantly easier for others to interface with your service, and react when things go wrong. New developers can easily see valid and invalid requests via a simple web browser, and programs can easily (and more robustly) extract error codes and act appropriately.

What is the best option for propagating error messages from REST-based web services? Are there other options beyond the four described here?


7 Comments

anonymous2
2003-12-01 05:14:38
404 is Resource Not Found
"we get back a 404 Error Code. From the developer perspective, have we actually typed in an invalid host name"


Obviously not, because then we'd be getting a DNS error. If it's simply the wrong hostname, rather than an invalid one, however...

anonymous2
2003-12-01 08:39:18
about option 1
It's perfectly possible to return a http error in the header, and a custom error message in the body. This is common practice for "custom 404 pages".


So I think some points are not really correct/complete, in that using http errors doesn't rule out human readable and "visible" information.

distobj
2003-12-02 06:12:24
Important issue, poor coverage
This is an important topic, but respectfully, I don't think you gave it the attention it deserves.


You didn't consider the implications to caching of using 2xx responses when a resource wasn't found, for example. That's quite a huge issue as any developer who's played with returning different response codes will tell you; as soon as you start returning errors on 2xx, you have to turn off caching, which reduces performance and scalability. Bookmarking has similar issues.


I would have recommended that you take a stab at determining what an "application error" is versus an "HTTP error"; you'd probably find that a particularly difficult task in fact. It's not nearly as simple as your first best practice item suggests.

ecerami
2003-12-02 06:28:53
RE: Important issue, poor coverage
Point well taken. I hadn't considered caching. And, differentiating HTTP errors from application errors is not a simple task. However, as a follow-up to your post, I wonder if you had any best practice suggestions of your own. Or, perhaps, it is hard to come up with one best practice, and it makes more sense to develop error handling on a case-by-case basis? Part of my reason for posting this was to stimulate debate on the topic, and I welcome any additional input you have. If others know of existing articles on this specific topic, please post links here too. Thanks.
richard_padley
2003-12-03 05:56:18
HTTP is an application protocol
Your recommendations for error handling expose a common misconception about REST and the web in general; HTTP is an application protocol, not a transport protocol. This fact means that the HTTP status codes apply to your application; your application is not separate from HTTP in the sense that it needs its own set of error codes.


Careful reading of the HTTP specification shows that the 400 series codes should be used to communicate application errors; in many cases either 400 Bad Request or 403 Forbidden should be used. These responses should have an entity body that contains an appropriate human readable error message, as you suggest. The scheme of having error codes is good practice, as this will allow the client to discern exactly why the request failed.


The idea of returning failure codes with 200 OK status is so bad that even the SOAP community did not make this mistake. 200 responses are cacheable, and the semantic is one of success not failure; read RFC2616.


Your article contains a number of mistakes which should be corrected;


1. 204 No Content is not an error code.


2. 401 Unauthorised should only be returned when using the standard authentication mechanisms defined in RFC2616 and RFC2617.


3. You seem to be under the impression that the entity body returned with a failure code is not displayed by the user agent; this is not the case, except with IE which appropriates 404 and 500 errors with its own page.

ecerami
2003-12-03 06:41:39
RE: HTTP is an application protocol
Thanks for your comments. I removed references to 204 and 401 codes in the article, and now only discuss the 404 Error Code. On the first comment I got re: this blog, I also changed "invalid" host name to "wrong" host name.


thompsonbry
2003-12-05 12:47:38
Status codes are important. Use them.
If you use "2xx" all the time, not only do you fail to provide the correct status codes for the request-response cycle but, as someone pointed out, you break caching and undermine the HTTP specification itself and make it impossible for clients to be sophisticated HTTP consumers.


REST using HTTP + XML is subtle (I've never tried it with another protocol). I continue to be impressed by the forethought that went into the HTTP/1.1 specification. Several times I have been tempted to subvert the specification, but longer consideration has repeatedly shown there to be a better solution which respects the HTTP specification and adheres to the REST architectural style.


There is an emerging generation of REST services. These services provide not only readable resources through HTTP/GET, but writable resources through HTTP/POST, PUT, and DELETE. I have found it useful to create an exception hierarchy based on the HTTP status codes which makes it easier for an application to report the right class of status code (2xx as success, 3xx as redirection, 4xx as client error, and 5xx as server error), but to also make it possible for the server to report the correct metadata along with the error response.


Maybe we do need more status codes, but I don't think that we will know until more REST services have been developed.