DEV Community

Cover image for Designing APIs for humans: Error messages

Designing APIs for humans: Error messages

Paul Asjes on September 14, 2022

Good error message, bad error message Error messages are like letters from the tax authorities. You’d rather not get them, but when you ...
Collapse
 
savagealex profile image
Alex Savage

Great blog! We are big fans of RFC7808 - application/problem+json If your making new APIs and are unsure on an error schema, we would recommend checking that out. Here is an example of it in action where we reference in the responses app.swaggerhub.com/apis/AdvancedCo...

Collapse
 
paulasjes profile image
Paul Asjes

I can see where you're coming from, but I think you're making a few assumptions here that can muddle the mixture.

For starters, I think it's a mistake to guess the intent of the end user. In the case of the "Customer not found" error, in 99% of cases it's caused by using the wrong API key. However we can't know that for sure. What if the user did mean to use their test keys, but accidentally used the ID of a live mode customer instead of the intended test mode one? In their case a 403: Test mode key was used is confusing and doesn't help them solve the problem. Returning a 404 and a message stating why it is a 404 is useful for 100% of cases, rather than 99%.

Secondly, while having good documentation is always a good idea and having terse headers rather than error messages might feel a little cleaner, we should take a step back and think about what we're trying to achieve. Is it more important that people read our docs or that they use the API?

I think it's the latter, and having valuable error messages helps push that goal forward.

Collapse
 
devangtomar profile image
Devang Tomar

That was a nice read! Liked, bookmarked and followed, keep the good work! 🙌

Collapse
 
iuliudumitrascu profile image
Julian Dumitrascu

I appreciate both that message and this team.

Collapse
 
yongchanghe profile image
Yongchang He

Nice article!

Collapse
 
harithzainudin profile image
Muhammad Harith Zainudin

Thank you for sharing! I've been facing this problem and not entirely sure what's the next step do. I'm glad that you show step by step. I'll surely implement it. thanks again! :D

Collapse
 
krizpoon profile image
Kriz Poon • Edited

Great advice! However, returning something like "Customer cus_Jop8JpEFz1lsCL not found" may be dangerous if the customer ID is an input parameter. It could be an attack. It’s worth to note that some kind of checks on the parameters must be carried out first.

Collapse
 
drdamour profile image
chris damour • Edited

Putting http status code in a message body is ugly. If the server responds with 404 but the status is 200 who do u believe? What youve defined is a message envelope, which is what we tried to kill when moving away from soap.

Http proxies exist in both client and reverse. It is impossible for you to guarantee as a service that you can return a message with this format. Nginx faulting in front of your service probably will return xml. That means any http client has to look at the http info and deal with that or theyll have critical failures. So every http client already has to look at http response codes and youve just made it so they have to look in 2 places, how inconvenient.

Even worse, this means u are assuming the response is being sent over http. I send way more resources over amqp than http. What does 404 mean in amqp? It means nothing!

The practice that DOES make sense is correlating responses with requests, this is even more important in amqp. But you should
Immediately abandon your status code envelope idea its been proven a poor idea going on 20 years

Collapse
 
adebiyial profile image
Adebiyi Adedotun • Edited

I think you have a point but I also think the first question from your reply defeats the purpose of what you're trying to point out. "...if my HTTP API communicates with non-humans..." is an assumption in the context of the purpose of the article which is already titled "Designing APIs for humans". So if the post is about "humans", I don't think making it about "non-humans" is a fair argument.

 
sarabellepalsy1 profile image
sarabellepalsy

Coming from cybersecurity, I am inclined to agree with Tamas. As much as I appreciate a helpful error message, too much help is a security risk, and API security is generally pretty lax as it is.

Collapse
 
slavius profile image
Slavius

Sorry to confront you this way but isn't API meant for applications? It's abbreviation of Application Programming Interface in the end. It's an interface to another application - a contract.
Why invest in making API human readable when it's meant to be consumed by machines/programs? The only case when you read these messages is in early prototyping stage or debugging. Even then a good documentation (e.g. SWAGGER/OpenAPI) is better suited.
You don't want your consumers to implement business/retry logic based on full-text english based error messages, do you?
How would you proceed in case of PROTOBUFF contracts? They are plain binary? What about human readabality in this case?

Collapse
 
paulasjes profile image
Paul Asjes

Good points! I would argue that in their base form APIs are meant to be consumed by applications, but in the end it's humans who implement and design these APIs. APIs tend to change over time, adding and removing features, which can lead to unexpected results. Having good error messages in this case means you have a better chance of fixing something if it breaks after your prototyping and debugging stage. After all, it's humans that do the fixing (for now).

I completely agree that good documentation is required, I would argue why not have good documentation and good error messages? Good error messages don't mean replacing essential information like status codes with full-text English, it means providing the extra information additionally to be helpful.

The beauty of designing with humans in mind is that all the extra information can be completely ignored by machines (or you) if you're confident that it all works. The status code (and hopefully an error type) will still be there, the rest is just gravy.

Collapse
 
slavius profile image
Slavius

API should not change to break stuff. You're supposed to issue a new version, mark your endpoints deprecated and let your consumers know about changes, replacement endpoints and new contracts (by pointing them to your documentation). If you keep only single version of your API and break it with incremental development changes you're doing it wrong.

You assume development/prototyping in form of "let's call this enpoint with any possible data in any possible way and let's see what error it throws to implement our business logic to handle all the cases". That's weird thinking in programming.

You invest time and effort to implement meaningful multi-language error messages for developers across all the world (happy translating your error messages to kanji, cantonese or hebrew and figuring out how to deliver them to their respective nationalities) just to help users debug/prototype solutions quickly in 0.00001% of your API call cases wasting bandwidth during production for the rest 99.99999% (applications neither don't care about meaningful error messages nor are willing to parse them to understand the source of the error) where a single error code and a Swagger documentation page would suffice.

Collapse
 
click2install profile image
click2install

I wouldn't advocate explicitly using 404 or 500. Leave them for the server to handle or they lose value. Not finding a resource isn't a 4xx, imagine if Google returned a 404 for no results when you type in a search. Be careful to not leak information with your messaging too. Secure messaging shouldn't be seen as an inconvenience.

Collapse
 
airtonix profile image
Zenobius Jiricek

It's awesome to see that you're recommending a Catalog of errors with metadata about the error.

Collapse
 
reconfence profile image
Recon Fence

Good information

Collapse
 
marcselman profile image
Marc Selman

Good article. I just don't understand why you return 'invalid_request_error' as the error type in the last sample. It's clearly a 404 error so the type should be 'not_found'.