4
\$\begingroup\$

I want to pass errors in errors in order to know exactly the chain of causes, but without augmenting or even reading any stack until this becomes really necessary. And this is how I would achieve this:

class RError extends Error {
    constructor(options = {}) {
        super();
        this.name = options.name;
        this.message = options.message;
        this.cause = options.cause;
    }
}

Example usage:

try {
    throw new RError({
        name: 'FOO',
        message: 'Something went wrong.',
        cause: new RError({
            name: 'BAR',
            message: 'I messed up.'
        })
    });
} catch (err) {
    console.error(err);
}

Here is the result:

{ FOO: Something went wrong.
    at RError (/Users/boris/Workspace/playground/index.js:5:9)
    at Object.<anonymous> (/Users/boris/Workspace/playground/index.js:13:11)
    at Module._compile (module.js:541:32)
    at Object.Module._extensions..js (module.js:550:10)
    at Module.load (module.js:458:32)
    at tryModuleLoad (module.js:417:12)
    at Function.Module._load (module.js:409:3)
    at Function.Module.runMain (module.js:575:10)
    at startup (node.js:160:18)
    at node.js:456:3 name: 'FOO', message: 'Something went wrong.', cause:
   { BAR: I messed up.
       at RError (/Users/boris/Workspace/playground/index.js:5:9)
       at Object.<anonymous> (/Users/boris/Workspace/playground/index.js:16:16)
       at Module._compile (module.js:541:32)
       at Object.Module._extensions..js (module.js:550:10)
       at Module.load (module.js:458:32)
       at tryModuleLoad (module.js:417:12)
       at Function.Module._load (module.js:409:3)
       at Function.Module.runMain (module.js:575:10)
       at startup (node.js:160:18)
       at node.js:456:3 name: 'BAR', message: 'I messed up.', cause: undefined } }

Since I couldn’t find anyone on the web who uses this technique, I’m wondering if there is any drawback to this approach.

Update: I’ve created two fiddles, an es5 and an es6 version (run, see results in the console).

Update: I’ve created a module: https://github.com/borisdiakur/rerror

\$\endgroup\$
  • \$\begingroup\$ What are you intending to get from this other than duplicated call trace information? \$\endgroup\$ – Mike Brant Oct 17 '16 at 18:54
  • 1
    \$\begingroup\$ @MikeBrant If you use libraries in between your function calls, you might end up with a stack trace which doesn’t contain a single line of your own code. So I intend to get as much fine grained information about each error in the error chain as possible, with each stack trace pointing to a line in my own code, without negative impact on performance. \$\endgroup\$ – borisdiakur Oct 17 '16 at 20:37
  • \$\begingroup\$ do we care about performannce when things go wrong? If an exception is catched. it should either be fixed. or blow uo the program. catching an error, throwing a new one that references the first one just smells imho. why not let the first one bubble up? this will get you the root cause aswell. no extra code \$\endgroup\$ – Pinoniq Nov 2 '16 at 7:38
  • \$\begingroup\$ @Pinoniq Yes, we care about performance even if things go wrong. It’s because we also have operational error caused by run-time issues (request timeouts etc.). Errors of that kind are handleable most of the time. You cannot fix them internally, you must handle them. And if you handle them, performance is important. Second: Why not let the first error bubble up? Because, again (see previous comment), if you use libraries in between your function calls, you might end up with a stack trace which doesn’t contain a single line of your own code. \$\endgroup\$ – borisdiakur Nov 2 '16 at 8:15
2
\$\begingroup\$

Cosmetically speaking, you can use the ES6 destructuring operator to make your arguments a bit more readable:

class RError extends Error {
    constructor({name, message, cause}) {
        super(name, message);
        this.cause = cause;
    }
}

As for drawbacks, the only one I can think of is having to do it yourself instead of having the language do it for you (Like, in example, in Java, which is where I guess this is coming from). So you'll need to remember to do it every time you have a rethrown exception. Personally, I wouldn't find this acceptable, as I would surely forget.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Maybe I can find a way to help those forgetful by creating an eslint rule which checks that cause is provided as an argument. \$\endgroup\$ – borisdiakur Oct 19 '16 at 10:16

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.