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.
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.
Depends on the product. It’s just something to think about when signaling errors. There is information for the API client developer, there is information for the client code, and there’s information for the user of the client. Remembering these distinct concerns, and providing distinct solutions, helps. I don’t think there is a single approach that is always correct.