Before going into examples, let’s make sure that we know some basics. I highlighted four types of errors in JavaScript:

Syntax errors occur at the interpretation time, they are considered as bugs and can never be handled properly because the code is just wrong by definition:

2. Program error, which is also a bug. For example, trying to read a property of undefined — it can be avoided by improving the code in this particular case by adding a conditional statement.

3. Runtime errors are called exceptions which happen during the execution:

invalid user input

failed to connect to a database

system out of memory

etc..

All correctly written programs should deal with runtime errors and as long as exceptions handled, they don’t necessarily indicate a bug or a serious problem. For instance, the runtime could throw a file not found exception, which might just mean that file has to be created first.

4. The logical error is when a developer makes a mistake in the logic of the program and it does not return an expected result. These kind of errors are the most difficult to track down because they depend on domain requirements.

A not properly handled runtime error is a bug. For instance, if runtime tries to connect to the database, but it gets a NetworkError which doesn’t have a registered handler, then the program will crash and that is a program error — the connection failure is a runtime error, but the failure to handle it is a program error.

In general handling errors in a centralized place should be avoided — every logical part should own handling the possible failures and response depending on exactly what has failed and why, but in cases when an exception doesn’t have a proper handler, it’s useful to have e centralized handler which sole purpose is to log the exception for the developer. We’ll take a closer look into handling uncaught exceptions in a browser, NodeJs, and Express.

Always create a hierarchy of application errors. Having an extensible and comprehensive exception handling for the application makes handling failures easier.

It’s also possible to handle the same error at several stack levels, for example when lower functions have nothing to do with an exception and just propagate the error up to the caller function, which could be the only one to know what to do with it, but that doesn’t mean that you should report all errors on the top level of the stack because the single function can’t know the context under which the exception has happened or what exactly has happened.

As a rule of thumb catch should handle only those errors that we expect to see in it, the rest should be propagated further with throw statement. An error name is used to define if an error is expected on the current level.

There are four basic methods to deliver error in NodeJS

throw the error (making it an exception).

the error (making it an exception). pass the error to a callback, a function provided specifically for handling errors and the results of asynchronous operations

pass the error to a reject Promise function

emit an "error" event on an EventEmitter

1. try .. catch .. finally construction

This construction is simply necessary in cases when it’s impossible to check the validity conditions before execution of code, for instance JSON.parse can’t check the correctness of string beforehand:

The runtime executes code in try section and if there are no errors it will skip catch section and move on to the next statement.

section and if there are no errors it will skip section and move on to the next statement. If there’s an error, the code execution in try section is interrupted and runtime jumps directly to the catch handler with an err param, which holds details of the error.

There are two moments to mention:

The construction above is able to catch only operational errors that happen on runtime by correctly written code, not programmer errors (like missing a closing bracket) which are bugs. It handles only synchronous code, the following code won’t work:

Here you can see we use a throw operator to throw an error object, returned by ajax function, and we manually throw Error object on our custom condition. Both cases would not work because at the moment when errors are thrown, the try/catch statement has already finished execution. The callback is invoked directly by NodeJS, with no try section around it which will cause an application to crash.

2. NodeJS error-first callback

NodeJS often uses a callback pattern where if an error is encountered during execution, this error is passed as the first argument to the callback:

The first argument of the callback is reserved for an error object. If an error occurred, it will be returned by the first and only err argument. The second argument of the callback is reserved for any successful response data. If no error occurred, err will be set to null and any successful data will be returned in the second argument.

The difference between error and exception.

An error is an instance of the Error class, it can be passed as a parameter to another function or it can be thrown. The error becomes an exception when it’s thrown. In JavaScript, it’s possible to throw anything, not only Error object but it’s not recommended because in this case exception will not hold a piece of potentially useful information like name, message, or call stack:

throw new Error('something bad happened');

but you can just as well create an Error without throwing it:

callback(new Error('something bad happened'));

and this is much more common in Node.js because most errors are asynchronous. As we’ll see, it’s very uncommon to need to catch an error from a synchronous function.

3. Promise.reject

Promise is a special object that holds its state. Initially, it’s in a pending or waiting state, then either fulfilled (completed successfully) or rejected (an error occurred).

I will not go into details about promises, just mention that you can add two types of callbacks for fulfilled and/or for rejected state:

promise.then(onFulfilled, onRejected)

It is also possible to add an error handler only with .catch(onRejected) instead of .then(null, onRejected) which works the same way.

Synchronous throw statement works as reject within a Promise body calling a corresponding callback:

4. Handle error event in EventEmitter

EventEmitter handles an event with the name error in a special way:

If error event occurs somewhere and it does not have a handler, then the EventEmitter generates an exception. Thus, seemingly so harmless, emit will exit the node process.