Handling Errors in Node.js (synchronous)
Tags: node, javascript
Published: 2021-08-04 22:39:24

---
title: Handling Errors in Node.js (synchronous)
published: true
date: 2021-07-30 00:00:00 UTC
tags: node, javascript
canonical_url: https://www.mariokandut.com/handling-errors-in-synchronous-functions-node-js/
---

Building robust Node.js applications requires dealing with errors in proper way. This is the second article of a series and aims to give an overview on how to handle errors in Node.js.

- [Introduction to Errors in Node.js](https://www.mariokandut.com/introduction-to-errors-in-node-js/)
- Handling errors in synchronous scenarios in Node.js (this article)
- [Handling errors in asynchronous scenarios in Node.js](https://www.mariokandut.com/handling-errors-in-asynchronous-functions-node-js/)

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#handling-operation-errors)Handling Operation Errors

Error handling can't be centralized in one part of the application, just like performance and security. When writing code a scenario (what might fail and what are the implications on the application) for an error has to be considered. It does not mean that the code will fail, but if could, it will sooner or later. The appropriate handling of errors depend on exactly what failed and why.

There are a few things that can be done when an error occurs:

- **Deal with error directly.** When it's clear how to handle an error, just do it directly.
- **Propagate the error to your client.** If you don’t know how to deal with the error, a simple solution would be to stop the operation, clean up whatever has started, and deliver the error back to the client.
- **Retry the operation.** It's useful to retry the operation with network errors and errors in remote services.
- **Blow up.** If there is an error, that is extraordinary it might be ok to log an error message and crash.
- **Log the error and do nothing else.** Sometimes there is nothing you can do (retry or abort), and the application can still work, there is no reason to crash. Just log the error.

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#handling-developer-errors)Handling Developer Errors

**There’s nothing you can do to handle a programmer error.** The code who should do something is broken, you can't fix broken code with more code. For example in a REST server a request handler throws `ReferenceError`, because there is a mistyped variable name.

The best way to handle developer errors is to **crash immediately and restart automatically** in an event of crash. The downside on this is that connect clients will be temporarily disrupted.

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#handling-errors-in-synchronous-functions)Handling Errors in Synchronous Functions

When an error is thrown in a synchronous function it can be handled with a `try/catch` block.

Lets refactor the example from the previous article to use `try/catch`:

```
class OddError extends Error {
  constructor(varName = '') {
    super(varName + ' must be even');
  }
  get name() {
    return 'OddError';
  }
}

function divideByTwo(amount) {
  if (typeof amount !== 'number')
    throw new TypeError('amount must be a number');
  if (amount <= 0)
    throw new RangeError('amount must be greater than zero');
  if (amount % 2) throw new OddError('amount');
  return amount / 2;
}

try {
  const result = divideByTwo(3);
  console.log('result', result);
} catch (err) {
  console.error('Error caught: ', err);
}
```

The output will be:

```
# ... file path

Error caught: OddError [ERR_MUST_BE_EVEN]: amount must be even.

# ... stack trace
```

With the `try/catch` pattern we were able to control the error output to the terminal. When the input to the function `divideByTwo()` is invalid an error will be thrown, and the execution doesn't proceed to the next line and instead jumps to the `catch` block. But rather than logging an error, we can check what type of error has occurred and handle it accordingly:

```
class OddError extends Error {
  constructor(varName = '') {
    super(varName + ' must be even');
  }
  get name() {
    return 'OddError';
  }
}

function divideByTwo(amount) {
  if (typeof amount !== 'number')
    throw new TypeError('amount must be a number');
  if (amount <= 0)
    throw new RangeError('amount must be greater than zero');
  if (amount % 2) throw new OddError('amount');
  return amount / 2;
}

try {
  const result = divideByTwo(3);
  console.log('result', result);
} catch (err) {
  if (err instanceof TypeError) {
    console.error('wrong input type');
  } else if (err instanceof RangeError) {
    console.error('out of range');
  } else if (err instanceof OddError) {
    console.error('cannot be odd');
  } else {
    console.error('Unknown error', err);
  }
}
```

Checking for the instance of error is flawed, consider the following change code:

```
try {
  const result = divideByTwo(4);
  result();
  console.log('result', result);
} catch (err) {
  if (err instanceof TypeError) {
    console.error('wrong input type');
  } else if (err instanceof RangeError) {
    console.error('out of range');
  } else if (err instanceof OddError) {
    console.error('cannot be odd');
  } else {
    console.error('Unknown error', err);
  }
}
```

We are calling `result()`, which is an error, since `result` is value returned from `divideByTwo(4)`, which should be 2. The output will be `wrong type`. This can lead to confusion, since the checking of errors was written to handle errors in `divideByTwo()`, and not from any other function in the `try` block.

To mitigate this it is recommended to use **duck-taping**. This means looking for certain qualities to determine what an object is. If it looks like a duck and sounds like a duck, it must be a duck.

Let's write a small utility function to add code to an error object:

```
function addCodeProperty(err, code) {
  err.code = code;
  return err;
}
```

Now we update the `divideByTwo()` function and the `try/catch` block with the updated if-statement for `err.code`.

```
function divideByTwo(amount) {
  if (typeof amount !== 'number')
    throw addCodeProperty(
      new TypeError('amount must be a number'),
      'ERR_AMOUNT_MUST_BE_A_NUMBER',
    );
  if (amount <= 0)
    throw addCodeProperty(
      new RangeError('amount must be greater than zero'),
      'ERR_AMOUNT_MUST_EXCEED_ZERO',
    );
  if (amount % 2) throw new OddError('amount');
  return amount / 2;
}

try {
  const result = divideByTwo(4);
  result();
  console.log('result', result);
} catch (err) {
  if (err.code === 'ERR_AMOUNT_MUST_BE_NUMBER') {
    console.error('wrong type');
  } else if (err.code === 'ERR_AMOUNT_MUST_EXCEED_ZERO') {
    console.error('out of range');
  } else if (err.code === 'ERR_MUST_BE_EVEN') {
    console.error('cannot be odd');
  } else {
    console.error('Unknown error', err);
  }
}
```

Now the error from `result()` is handled properly. The output will be:

```
# ... file path

Unknown error TypeError: result is not a function

# ... stack trace
```

**IMPORTANT:** `Try/Catch` cannot catch errors that are thrown in a callback function that is called later.

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#tldr)TL;DR

- When an error is thrown in a synchronous function it can be handled with a `try/catch` block.
- `Try/Catch` cannot catch errors that are thrown in a callback function that is called later (i.e. with `setTimeout()`)
- Differentiating by `instanceof` is flawed, a code property should be added to the error to differentiate (see example above).

Thanks for reading and if you have any **questions** , use the **comment** function or **send me a message** **[@mariokandut](https://www.twitter.com/mariokandut)**.

If you want to know more about **[Node](https://www.mariokandut.com/tags/node)**, have a look at these [Node Tutorials](https://www.mariokandut.com/tags/node).

**References (and Big thanks):**

[JSNAD](https://trainingportal.linuxfoundation.org/learn/course/nodejs-application-development-lfw211/handling-errors/handling-errors),[MDN Errors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error),[MDN throw](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw),[Node.js Error Codes](https://nodejs.org/api/errors.html#errors_node_js_error_codes),[Joyent](https://www.joyent.com/node-js/production/design/errors)