I prefer simplicity and using the first example but I’d be happy to hear other options. Here’s a few examples:
HTTP/1.1 403 POST /endpoint
{ "message": "Unauthorized access" }
HTTP/1.1 403 POST /endpoint
Unauthorized access (no json)
HTTP/1.1 403 POST /endpoint
{ "error": "Unauthorized access" }
HTTP/1.1 403 POST /endpoint
{
"code": "UNAUTHORIZED",
"message": "Unauthorized access",
}
HTTP/1.1 200 (🤡) POST /endpoint
{
"error": true,
"message": "Unauthorized access",
}
HTTP/1.1 403 POST /endpoint
{
"status": 403,
"code": "UNAUTHORIZED",
"message": "Unauthorized access",
}
Or your own example.
This is the right answer imo. While it might be an overkill for sth like 404s, it’s amazing for describing different bad requests.
This one looks nice. Very detailed.
The documented one. It is hell to work with APIs where only the happy path is documented.
JSON Problem Details
https://datatracker.ietf.org/doc/html/rfc9457
- It has a specification, so a consumer of the API can immediately know what to expect.
- It has a content type, so a client sdk can intelligently handle the response.
- It supports commonly needed members which are a superset of all of the above JSON examples, including type for code and repeating the http status code in the body if desired.
- It is extensible if needed.
- It has been defined since at least 2016.
This specification’s aim is to define common error formats for applications that need one so that they aren’t required to define their own …
So why aren’t you using problem details?
Respect the Accept header from the client. If they need JSON, send JSON, otherwise don’t.
Repeating an HTTP status code in the body is redundant and error prone. Never do it.
Error codes are great. Ensure to prefix yours and keep them unique.
Error messages can be helpful, but often lead developers to just display them in the frontend, breaking i18n. Some people supply error messages in multiple languages, depending on the Accept-Language header.
but often lead developers to just display them in the frontend
Oh boy I feel this one.
My API is meant for scripting (i.e. it’s for developers and the errors are for developers), but the UI team uses it and they just straight display the error from their HTTP request for none technical people which might also not get to know all the parameters actually needed for the request.
And even when the error is in fact in my code, and I sent all the data I need to debug and replicate the error, the users can’t tell me because the UI truncates the response, so the user only sees something likeError in pe1uca's API: {"error":"bad request","message":"Your request has an error, please check th... (truncated)
. So the message gets truncated and the link to the documentation is also never shown .-.To be fair if it’s an exceptional error message (i.e. database timeout; not incorrect password) I don’t think i18n matters that much. Most people will just be googling the error message anyway, and if not it should be rare enough that using Google translate isn’t an issue.
If anything i18n makes things way worse for everyone. Ever tried to diagnose a semi-obscure Windows or Android error on a non-English locale? Pretty sure that’s one of the activities in the inner circles of Hell. Bonus points if the error message is obviously machine-translated and therefore semantically meaningless.
Unique error codes fix this if they remain visible to the user, which they usually don’t because Mr Project Manager thinks it looks untidy.
I’m a data engineer, and have seen an ungodly ammount of 200-but-actually-no-stuff-is-broken errors and it’s the bane of my life!
We have generic code to handle pulling in api data, and transforming it. It’s obviously check the status code, but any time an API implements this we have to choose between:
- having code fail wierdly further down the line because can’t parse the status
- adding in some kind of insane
if not response.ok or "actually no there's an error really" in response.content
logic
Every time you ignore protocols and invent your own, you are making everyone sad.
Will take recommendations of support groups I can join for victims of terrible apis.
The status code that gets returned should be the status code of the messenger and not the data. If you want to add a status code about the data, then please do.
If something can return null and empty and it’s valid, that is not a 404. That is a 200.
As far as a 403, the messenger is telling you that you shall not pass. There is no data. 403 is appropriate here. The return response can be anything since 403 is pretty self explanatory, but I would probably return json to be consistent. I would also use the field message. Something like the first one for this use case only.
In other cases where i do get data, I would use data, message, status (optional). But status in the json response would be status about the message.
Giving back a 200 for an error always makes me bristle. Return correct codes people. “But the request to the web server was successful!”
I worked on a product that was only allowed to return 200 OK, no matter what.
Apparently some early and wealthy customer was too lazy to check error codes in the response, so we had to return 200 or else their site broke. Then we’d get emails from other customers complaining that our response codes were wrong.
I use this big expensive simulator called Questa, and if there’s an error during the simulation it prints
Errors: 1, Warnings: 0
and then exits withEXIT_SUCCESS
(0)! I tried to convince them that this is wrong but they’re like “but it successfully simulated the error”. 🤦🏻♂️We end up parsing the output which is very dumb but also seems to be industry standard in the silicon industry unfortunately (hardware people are not very good at software engineering).
That’s when you use different exit codes. 1 for failure during simulation, 2 for simulation failed.
Shame they wouldn’t listen.
I generally agree, but with robocopy they went too far with this, because the status code doesn’t work the way you expect, and you’ve got to script around it.