My organization is developing a Web API server. We will also develop the only clients authorized to access the API. We will follow the usual technique of returning Bad Request (400) errors for data sent from a client to the server when the data doesn't pass validation at the server. There will be a number of validation conditions that can be violated, so we want to return an error as the response content specifying which condition was violated in each case.

This is often done by using text error messages as the response content but because both sides of the web communication are being developed together, we are considering using integer error codes as the response content to avoid the nuisance of "stringly typed" data. Are there pitfalls to that approach, or what is the best practice in this case?

PS Addendum -- The clients are embedded systems for use by end users who may not be computer-savvy. Any validation error represents a bug and if occurring in a production environment would also be logged, not something that would be meaningful to display to end users.

PPS -- I think I've just given a reason why no error code needs to go to the client other than a general failure indication. The error code should just be logged.

  • 2
    Why not do both? Include not only an error code in your return JSON for client software to consume, but also a human-readable description of the problem. Apr 22, 2019 at 20:24
  • 2
    If there is any chance that deployment of server and client is not in sync, having stringy error messages will enable the client to display messages that have been introduced after the client was rolled out. If the validation checks are complex and subject to refinement after the application has been rolled out, there's a good chance that this could happen. Apr 22, 2019 at 20:34
  • I have to agree with Robert. Even if the end-user is not computer-savvy, whoever is reading the logs will see only a number. It forces such a poor soul to recall all the codes and the mappings to meaningful descriptions "code 1 means A, code 2 means B, code 999 means ...". So, why don't you do like Oracle? ORA-00001: unique constraint (string.string) violated*.
    – Laiv
    Apr 23, 2019 at 9:40
  • Please! please! please! include specific error messages in responses. I am currently consuming a third-party API for a customer and it gave me a 400 for all operations. Because the error message indicated a specific configuration issue, we resolved it in a few hours instead of struggling with it for days and having to contact the vendor to look up an error code.
    – Dan Wilson
    Apr 23, 2019 at 10:17
  • @Robert Harvey, crisp and to the point. If you'll add this as an answer, I'll accept it. Thanks to all contributors. Apr 24, 2019 at 14:18

3 Answers 3


Using error codes instead of text messages makes sense, in my opinion, in a context where they are used internally. Things would be very different if you were developing a public API—other developers won't enjoy working with cryptic codes instead of human-readable, explicit error messages. The benefit, with integers, is that you're saving some bandwidth; are the savings that important is a separate question (and I hope you know the answer to it).

I'm not sure what do you mean, however, by “stringly typed” data. Since your question is in a context of a Web API, I suppose that you're communicating through XML or JSON, which means that everything is a string. When you're sending a error code 260, you're sending something like:

HTTP 400
    "error": "Bad request",
    "code": 260

as opposed to [0xF0, 0x02, 0x00, 0x00] (or the other way around, depending if you use Big-endian or Little-endian), isn't it? So it's a string. It is presented to your application as an integer, but only because the serialization library converted it to one. In the same way, it could give you a value from an enum.

Back to your question about the possible drawbacks, here they are:

  • You're increasing a risk for developers to make a mistake. It's not that difficult to make a mistake of thinking that error 422 means “Customer ID is outside the range of allowed values,” while in fact it means “The customer with the specified ID was found, but is currently marked as disabled.” If the error message is “CustomerIsDisabled,” it would be much more difficult to make the same mistake.

    Small mistakes like that often cost quite much. It is not unusual to waste several hours trying to debug an issue, and then finding that you're on a completely wrong track because you thought something which appeared to be something else.

    For this reason alone, it would be preferable to use explicit error messages as much as possible.

  • You're maybe making it impossible to add metadata. If (and only if) the response contains a single value (either a error message or a error number), then the string variant has a benefit of allowing extra data to be added within the error itself.

    If you can add metadata side by side with the error code, then the error code is fine:

    HTTP 400
        "error": "Bad request",
        "code": 260,
        "error-info": {
            "allowed-limit": 100000,
            "currently-used": 100027,
            "blocked-until": "2019-05-24T14:52:50Z"

    This, by the way, would be better than a simple error message such as:

    Operation blocked until 2019-05-24T14:52:50Z, because the user exceeded the allowed limit of 100.000 changes for the day, the current usage being 100.027.

    because if the consumer wants to do something with the metadata, it would need to parse the error message and extract the data, instead of just accessing it as is.

  • using an enum instead of explicitly writing the error code would address the first drawback.
    – user174739
    Jun 23, 2019 at 2:09

Semantic Error Codes

If your embedded devices need to react to errors in an intelligent way, passing back a number (or series of them) makes sense. Its a simple way of communicating what the problems where.

Oh there was a x41, x28, x23582, and xad12 errors. So highlight the x field, display a bell next to this other field, and internally bump a fraudulent use bit so that later forensics can show that the device was used outside of contract.

Human Error Codes

Error strings can become quite elaborate, and often need to be updated for specific cultural and regional requirements. Some of these requirements may actually instigate legal proceedings.

As such relying on the wholly unreliable system update of the embedded devices to fix error messages may just be to slow. Even if for some reason you could guarantee the update, is is feasible? Embedded devices are not known for having infinite disk space. It might just work out to be easier to just send the entire tailored error message.

Although there is a middle ground. Send the Semantic Error Codes, and allow the embedded device to lookup an error message locally from its own table. Unless the Semantic Error Code was accompanied by a custom message, in which case just show the custom message.


For HTTP 1.1 responses, I like to transmit the human-friendly text as the HTTP status line's reason phrase. They're usually pretty clear by design, but they can be modified to be more specific and descriptive. It forces brevity (though there is no max length defined in RFC 2616 or RFC 7230), and prevents the need of a preamble/wrapper for the response body.

HTTP/1.1 401 You are not logged in

The client-side will already have a conditional for the HTTP status; so it would prevent needing a separate conditional later. If the client-side needs more verbose text or a customized design, they can use the conditional that is already there, exactly where it's needed.

The biggest downside to this is that the reason phrase has been removed from the HTTP/2 spec. From my experience, a large portion of modern web services will need modification when swapping from HTTP/1.x to HTTP/2; so HTTP/1.1 is not going away any time soon.

  • I'm not sure that this is what the OP is looking for. It sounds like the desired behavior is a valid response that just happens to report an error. This idea would return an error response, which can have other (stronger) ramifications. Jun 23, 2019 at 3:19

Not the answer you're looking for? Browse other questions tagged or ask your own question.