Errors can come from many different sources - third-party services, libraries, environment, etc. When an error prevents a user from performing their desired action, these diverse errors should be communicated to the user, so they their action failed and can take steps to solve the issue.

Are there any common strategies for normalizing diverse errors? As an example scenario, imagine an application that consumes data from the local machine, a database, and a third-party web service. This application provides some kind of user interface (CLI, web app, desktop app, etc.), and when an error occurs it informs the user.

In my mind, the key issues are:

  • Handling errors consistently
  • Effectively communicating error to users and to developers

A good answer would be something like a common pattern, a sample implementation, technical overview, or something more broad like a link to a popular video on the subject.


Handling errors consistently

Errors can take the form of a thrown exception, a proper Error object, a plain object, an optional argument to a callback, a Promise, a special return value, a simple string, etc. At some point, all of these have to be converted into some message for the user. How do you convert between all the various forms, where does this conversion happen, where should it be handled/forwarded to the UI, and what should an error be/contain?

From a practical perspective, I think it makes sense to do the conversion in one place, maybe just before the error is sent to the user. In the case of web applications, this would be middleware called after a response is generated, but before it is sent to the browser. This middleware could normalize the response so it follows some common form (like a JSON object with title, description, original-error, and backtrace properties - see "Effectively communicating error to users and to developers" below), and log the relevant information for debugging purposes.

Also, sometimes multiple errors occur simultaneously (such as validation errors), so error objects/responses/etc. should represent a list of errors – in most cases, there will probably be a single item in this list.

To complicate matters, sometimes the UI code has errors, and this needs to be recorded for debugging purposes. Again, in the case of the web application, the webserver could include an HTTP route that simple accepts error data from the browser and logs this information to the same place as the errors raised by the server.

Effectively communicating error to users and to developers

It's probably useful to distinguish between an error message for developers/support, and an error message for users, but the root cause is still just a single error. So this single error should contain enough information for developers/support to quickly identify the problem, but also enough information so a message can be shown to users that makes sense – hopefully with steps to workaround/fix the issue.

That being said, it seems like a good error would contain a technical description for developers (with backtrace if possible), a brief message for users, and a detailed description of the problem (with workarounds if possible). Security should be considered (backtraces can reveal inner-workings of your program).

Since JSON is a language-/protocol-agnostic format, here's what an error from a REST API may look like:

   "errors": [{
      "title": "User already exists",
      "description": "Somebody's already using that username - please try a different username",
      "original-error": {
         "message": "DatabaseError entity exists",
         "backtrace": "... use your imagination ..."
   }, {
      "title": "Cannot use temporary email accounts",
      "description": "Email addresses with the disposable email provider, mailinator.com, cannot be used. Please use a different address."
      "original-error": "DocumentValidationError mailinator.com is not supported"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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