0
function doFoo() {
    throw new Error("no foo for you");
}
    
try {
    doFoo();
} catch(e) {
    throw new Error("doFoo() for shame!", e);
}

Is there any sane way to do this in JavaScript?

My specific objectives for the particular use case I'm asking this question are the following, in this order:

  1. Follow standards, or at least best practices
  2. Throw a new exception/error/whatever you want to call it (not just rethrow the thing I just caught); I'll call this an exception from now on
  3. Preserve the message in the previous exception (I'd like to preserve the stack trace, but that's not as important for this particular use case)
  4. Add extra metadata to the previous exception
  5. Preserve the stack trace of the previous exception (nice to have, not really important to me right now)
Bogdan Stăncescu
  • 5,320
  • 3
  • 24
  • 25
  • Question - do you want to throw an error with a new message or just rethrow? If it's the latter just `catch(e) { throw e }` – Phix Nov 03 '20 at 19:51
  • Please revisit the title of the question: I specifically want to preserve the inner exception. – Bogdan Stăncescu Nov 03 '20 at 19:53
  • The duplicate I found is kinda old but has believe all you are looking for. – Bergi Nov 03 '20 at 20:24
  • @Bergi, not only it's old, but it also does not satisfy the very first of my objectives. Please remove the duplicate. – Bogdan Stăncescu Nov 03 '20 at 20:26
  • @BogdanStăncescu How do you know? I can tell you that there is no implementation for this in the ECMAScript standard, and all the libraries (as well as their possibly more modern/popular cousins) in the duplicate do follow the best practice of wrapping the inner error as a property of a new error. – Bergi Nov 03 '20 at 20:29
  • @Bergi I assume you're an admin of some sort on SO. Whenever you act here, you have to decide if you act as an admin, or as an expert answering questions. Right now, you're wearing both hats, and that's not ethically appropriate. If you're an admin, you close a question as a dupe because it's an obvious dupe – but you're not doing that right now, because you're arguing using expert arguments ("how do you know? there is no ECMAS standard"). OTOH, you're not answering as an expert, either, because you forcefully closed the question as a dupe, instead of submitting an answer. Please dedupe it. – Bogdan Stăncescu Nov 03 '20 at 20:37
  • I think your question is a bit too broad: "*How to rethrow preserving the inner exception?*" is an exact duplicate. "*What is the standard way to do this?*" can (assuming it's not a rephrasing, but asking for normative resources) be answered in a comment with "There is none". And finally, "*What is the best practice?*" is opinion-based and off-topic. Out of the three, tried to choose the most useful one that provides a solution. – Bergi Nov 03 '20 at 20:48
  • And no, I'm just a normal user with lots of JS experience. If you can explain how the duplicate does not answer your main question, I'll happily reopen. If you want to discuss how I handled this, please [raise it at meta](https://meta.stackoverflow.com/questions/ask) and point me a link there. Or even a moderator flag. – Bergi Nov 03 '20 at 20:48
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/224058/discussion-between-bogdan-stncescu-and-bergi). – Bogdan Stăncescu Nov 03 '20 at 20:50

1 Answers1

3

How about a simple wrapper ?


class RootCause extends Error {
    constructor(message, error) {
        super(message);
        if (error) {
            this.cause = error;
        }
    }
}

function doFoo() {
    throw new RootCause("no foo for you");
}

try {
    doFoo();
} catch(e) {
    throw new RootCause("doFoo() for shame!", e)
}


/** Prints:
RootCause [Error]: doFoo() for shame!
    at Object.<anonymous> (/private/tmp/test.js:17:11)
    at Module._compile (internal/modules/cjs/loader.js:1138:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
    at Module.load (internal/modules/cjs/loader.js:986:32)
    at Function.Module._load (internal/modules/cjs/loader.js:879:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47 {
  cause: RootCause [Error]: no foo for you
      at doFoo (/private/tmp/test.js:11:11)
      at Object.<anonymous> (/private/tmp/test.js:15:5)
      at Module._compile (internal/modules/cjs/loader.js:1138:30)
      at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
      at Module.load (internal/modules/cjs/loader.js:986:32)
      at Function.Module._load (internal/modules/cjs/loader.js:879:14)
      at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
      at internal/main/run_main_module.js:17:47
**/
36ve
  • 513
  • 4
  • 12