Wrapping Errors
Tags: javascript, errors
Published: 2019-05-31 05:33:33

---
title: Wrapping Errors
published: true
tags: javascript,errorhandling
canonical_url: https://blog.parametricstudios.com/posts/wrapping-errors/
---

When performing I/O in Node.js servers, there is a lot of error handling to deal
with. I always look to add as much context to errors as I can because it greatly
reduces the time it takes me to find, understand, and fix bugs.

We won't go into an error propagation strategy here _(that will be part of some
of the upcoming material)_, but I wanted to share a quick idea for how to
decorate errors with context as they propagate through the stack.

## Native Error

JavaScript comes with a few built-in error classes. They include:

- `Error`: a base class for exceptions.
- `EvalError`: an error regarding the global `eval()` function.
- `InternalError`: an error that occurred internally in the JavaScript engine.
- `RangeError`: an error when a value is not in the set or range of allowed
  values.
- `ReferenceError`: an error when a non-existent variable is referenced.
- `SyntaxError`: an error when trying to interpret syntactically invalid code.
- `TypeError`: an error when a value is not of the expected type.
- `URIError`: an error when a global URI handling function was used in a wrong
  way.

While we may sometimes find usages for specific built-ins like the `RangeError`,
most user-defined exceptions make use of the base `Error` class. Its properties
include a `name`, `message`, and `stack`. In Node.js, error instances
additionally contain a `code` which identifies various error types including
underlying system errors.

Creating our own error _(and throwing it)_ looks something like this:

```js
function parseIntStrict(numLike) {
  let n = parseInt(numLike, 10);
  if (!Number.isInteger(n)) {
    throw new Error(`unable to parse input as integer: ${numLike}`);
  }
  return n;
}
```

Here we create and throw an error when `parseInt` returns `NaN` values. The
message in the error is about as specific as we can make it without more
context. This function might be called to convert user input for updating a
product quantity in a shopping cart, parsing a field from a csv file, etc.--we
can't know how it will be used.

### In Context

Let's say we are writing an api that takes a csv file and parses inventory
quantities from its row fields. We're going to use our `parseIntStrict` function
when we parse a product's inventory count in the file. We might think of our api
call stack like this:

- `[4]` Parse quantity field
- `[3]` Parse csv row
- `[2]` Read file by lines
- `[1]` Map request to csv file on disk
- `[0]` Receive network request

There are a number of steps here before we use the `parseIntStrict` function.
Ideally, if a call to our function fails, we would like to know our request id,
the name of the csv file on disk, the row index, and the field that caused the
failure. If we have all that information, we can go straight to solving any
broken functionality instead of exhausting valuable time diagnosing the source
of the problem.

## Wrapping Errors

In order to achieve our goal of adding context to an error, we need a way to
decorate errors and then bubble them back up the stack. One way we could do this
is by adding a message representing the current context. We don't want our
message to corrupt the original message in any way. This discourages us from
attempting to manipulate the original error's message, but since we aren't
always guaranteed error producers use the `Error` class to generate exceptions,
this isn't an option in the first place.

Let's extend the `Error` class so we can add a reference to the untouched,
original error value. This will let us add our contextual message and keep a
reference to the original error value regardless of it being an `Error` instance
or not.

```js
class WrappedError extends Error {
  constructor(message /* context */, previous /* original error value */) {
    // First we have to call the Error constructor with its expected arguments.
    super(message);
    // We update the error's name to distinguish it from the base Error.
    this.name = this.constructor.name;
    // We add our reference to the original error value (if provided).
    this.previous = previous;
  }
}
```

We aren't changing much from the base `Error` class. We'll get the default
`Error` behaviors for free since we inherit _(generating stack traces,
stringification, etc.)_, but now our error type can reference a previous error.
If we use a `WrappedError` to generate our first exception, the previous error
will be `undefined`. If we create an instance passing in an `Error` or a literal
value, `previous` will maintain a reference to it. Most interestingly, if we
create an instance passing in another `WrappedError` instance, we connect a
chain of errors together.

When we start using our `WrappedError` throughout our application code, we
essentially transform our error values to linked lists. The list head would be
our current error's position in the stack, or its **context**. We could traverse
the list by checking for the existence of a previous error, and if it exists,
checking to see if it's an instance of `WrappedError`, which guarantees us a
`previous` field we can inspect to continue the traversal.

Let's look at how we might perform a traversal to get at the root error
value--be it a `WrappedError`, `Error`, or otherwise. We'll use a computed
property--an
[object getter method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)--to
allow us to compute and access the `root` value like a property.

```js
class WrappedError extends Error {
  // ...

  get root() {
    // Base case, no child === this instance is root
    if (this.previous == null) {
      return this;
    }
    // When the child is another node, compute recursively
    if (this.previous instanceof WrappedError) {
      return this.previous.root;
    }
    // This instance wraps the original error
    return this.previous;
  }
}

let error1 = new Error("the first error");
let error2 = new WrappedError("the second error wraps #1", error1);
let error3 = new WrappedError("the third error wraps #2", error2);

console.assert(
  error2.root === error1,
  "the root of error2 should be strictly equal to error1"
);
console.assert(
  error3.root === error1,
  "the root of error3 should be strictly equal to error1"
);
// Passes if no error appears in the console
```

You can test this example in
[this repl here](https://repl.it/@alexsasharegan/Wrapping-Errors-1), but suffice
it to say that the strict equality checks are all true. There's a lot we can do
here, but simply logging with `console.error` produces this output in Node.js
_(from the same repl)_:

```sh
{ WrappedError: the third error wraps #2
    at evalmachine.<anonymous>:27:14
    at Script.runInContext (vm.js:74:29)
    at Object.runInContext (vm.js:182:6)
    at evaluate (/run_dir/repl.js:133:14)
    at ReadStream.<anonymous> (/run_dir/repl.js:116:5)
    at ReadStream.emit (events.js:180:13)
    at addChunk (_stream_readable.js:274:12)
    at readableAddChunk (_stream_readable.js:261:11)
    at ReadStream.Readable.push (_stream_readable.js:218:10)
    at fs.read (fs.js:2124:12)
  name: 'WrappedError',
  previous:
   { WrappedError: the second error wraps #1
    at evalmachine.<anonymous>:26:14
    at Script.runInContext (vm.js:74:29)
    at Object.runInContext (vm.js:182:6)
    at evaluate (/run_dir/repl.js:133:14)
    at ReadStream.<anonymous> (/run_dir/repl.js:116:5)
    at ReadStream.emit (events.js:180:13)
    at addChunk (_stream_readable.js:274:12)
    at readableAddChunk (_stream_readable.js:261:11)
    at ReadStream.Readable.push (_stream_readable.js:218:10)
    at fs.read (fs.js:2124:12)
     name: 'WrappedError',
     previous: Error: the first error
    at evalmachine.<anonymous>:25:14
    at Script.runInContext (vm.js:74:29)
    at Object.runInContext (vm.js:182:6)
    at evaluate (/run_dir/repl.js:133:14)
    at ReadStream.<anonymous> (/run_dir/repl.js:116:5)
    at ReadStream.emit (events.js:180:13)
    at addChunk (_stream_readable.js:274:12)
    at readableAddChunk (_stream_readable.js:261:11)
    at ReadStream.Readable.push (_stream_readable.js:218:10)
    at fs.read (fs.js:2124:12) } }
```

Not bad, right?

## Open For Extension

The contextual references we're able to create with this provide a lot of
opportunities for extension. Depending on the needs of your application, you
might consider:

- Add a `spans` getter to compute an array of all the stringified errors
- Memoize the getters by overwriting them after first access using
  [`Object.defineProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)
- Make the list iterable by implementing
  [`WrappedError.prototype[@@iterator]()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator)
- Make iterator methods like `forEach` or `map` that implement
  [pattern matching for your custom data types](https://blog.parametricstudios.com/posts/pattern-matching-custom-data-types/)
  _(like WrappedError, Error, and any)_

I've implemented the `spans` property in the
[repl link](https://repl.it/@alexsasharegan/Wrapping-Errors-1) if you're
curious. If you think of new applications or features for this idea, you can
find me on Twitter at [@alexsasharegan](https://twitter.com/AlexSashaRegan).