History of humanity is written by errors. May it be false judgement, dumb luck or other circumstances - errors have brought us to where we are today. And we should embrace errors more instead of crucifying them for what they are. After all, that's how we learn to do things differently next time. Errors in software development are no exception. This of course includes error handling in APIs.
As Phil Sturgeon notes in this blogpost: "Dealing with the Happy Path™ in an API is pretty easy". Nonetheless we do have to deal with the not so nice parts of API communication and that - among a few other things - means to talk about the right error handling. Since errors can definitely happen in any API, communicating those errors in a clear and concise way is something API providers shouldn't be negligent about.
When it comes to a good handling of error cases in an API, there are a few best current practices you should have in mind. First and foremost - you should use the correct HTTP status code in the response, so an API consumer can see straight away, that "something bad" has happened. When you are returning detailed information about the error you should encapsulate it in an error object, so it doesn't mix with other content that is being returned.
To make it easier for consumers of your API to implement error handling in their client you should think about using a common pattern for defining the contents of your errors. A great but unfortunately at the moment not so frequently used standard comes from RFC 7807 - "Problem Details for HTTP APIs", which introduces a new content-type "application/problem+json" and with it a set of attributes that an API should return in case of an error (BTW: the content-type "application/problem+xml" also gets introduced in the same RFC).
There are however a few caveats I think should be mentioned:
- The attributes are in the responses' root. Therefore error based "meta" information and normal response content mix.
- The format can only return information about a single error case. This means after fixing one error you could get a new one when making another request.
- There is no way of conveying the fact that a "soft error" / "warning" has happened.
The fact that programming languages like e.g. Java and PHP have a logging concept that contains a severity is something that's absolutely neglected in todays APIs. At the moment there is no standardised way of reporting warning type of errors back to a client. However it is not unusual to return information in a response that wants to convey the fact that "your request was successful, but something has happened and you should have a look at it".
At shipcloud, the company I work for, we are dealing with warning type of errors on a daily basis. The web services (and other systems) that we are communicating with are returning informational parts in their responses that we are currently not returning to the clients talking to our API. This is something that we are willing to change for version 2 of our API. Which is why a few months ago I contacted Erik Wilde and Mark Nottingham, the authors of RFC 7807, to find out if they have thought about use cases like ours, when they were writing their RFC, or at least have a suggestion as to how one would communicate warnings in an HTTP API. As it turned out there are actually no clear answers to my questions.
Fortune had it, that Erik and I were both speaking at the API Conference in Berlin in October of 2019. I had used the idea about making a "Call for a better error communication in APIs" to give a talk about this topic, since it was already floating around in my head in the first half of the year.
So after a little discussion with Erik, we started working on an Internet Draft (I-D), that would address the issue of returning warning information through an API. The idea behind the draft is to supply a way of communicating both errors and warnings alongside. So APIs won't be limited to returning just one of them. I didn't want to deprecate application/problem+json, since it was already a thing that had been adopted by companies like Adidas and Zalando. And reworking the whole thing wouldn't have helped anyone. Also introducing yet another content type would make it impossible to use our I-D and the RFC in combination.
The best chance to have an idea adopted by a community is when you are working with concepts that are already on the market. This also makes it easier for http clients to incorporate your ideas, since they are already built around the original concept.
Which is why we wanted to use the Warning header, which was introduced in RFC 7234 "a long time ago". Its main purpose was to "warn about possible incorrectness introduced by caching operations or transformations applied to the payload of the message". But the RFC also states that "Warnings can be used for other purposes, both cache-related and otherwise. The use of a warning, rather than an error status code, distinguishes these responses from true failures." So using the standard as a starting point for our idea to communicate warning information sounded like the best idea at the time.
If you take a look at version 1 of our I-D you can see from our example for "soft errors with data" that we're introducing a new warn code "246" which if present would indicate that there is an error message in the body of the response. Within the body you can then have an entry "warnings" that contains more details. It is designed as an array of JSON objects, which are following the RFC 7807 pattern to specify what has gone wrong. You can also combine this with the content-type "application/problem+json" to be able to return "hard errors with warnings" and have both be present at the same time. Since our warnings are encapsulated in the warnings entry.
So after uploading our I-D to the IETF the unimaginable happened. We found out that the due diligence we did wasn't good enough. As it is with technology, sometimes things go faster than one can anticipate. There was already another RFC in the making that would render our idea obsolete. There would have been a chance to find this out, if we had scanned all current and upcoming RFCs and I-Ds in detail. The reality is, you can't. What I had done was checking RFC titles to see if there's something obviously contradicting our ideas and nothing stuck out. So we submitted our draft and found out about the changes through an issue in the github repository that hosts our I-D.
The reality was the warning header that we wanted to use to convey the fact that there is warning information present in the body of a response was supposed to be removed in the next version. Something we also could have found out earlier if we had screened the IETF http mailing list. We started to gather more information, trying to find out more from the authors and see if there was a way of keeping the header, since it is the epitome of what we're trying to do and there seemed to be no better way of communicating the information. Unfortunately, everyone behind the new RFC was dead serious, to have the header removed, because it was used in a multitude of ways in the past. A lot of them in ways that didn't reflect the original intent.
Every failure gives room for improvement. And now it is here! Version 2 of our internet draft can now be reviewed at the IETF website. Instead of using the warning header we are now proposing a new http header field called "Content-Warning". The header value can have a list of entries that each have a "type" and a "date" value. With the introduction of the new header we are also registering a Content-Warning type called "embedded-warning", which indicates that more information about an error can be found in the responses' body. The body itself hasn't changed much. You still get to return a warnings object that follows the RFC 7807 pattern.
One thing I'd like to point out is that by using the new Content-Warning header with the type embedded-warning you can still return "application/problem+json" as your content type. Your error data within the body is formatted accordingly and you can combine this error data with the warnings data that we are proposing. This way you can return errors and warnings within a single http response, which means that you are able to return informational data that was gathered "along the way", even if you encounter a server error.
If you have ideas and/or suggestions about this subject and/or the internet draft, feel free to contact us.