19

Is there a preferred way to include inner exceptions when throwing exceptions in JavaScript?

I'm relatively new to JavaScript coming from a C# background. In C#, you can do the following:

try 
{
  // Do stuff
}
catch (Exception ex)
{
  throw new Exception("This is a more detailed message.", ex);
}

In the samples I've seen in JavaScript, I haven't been able to find how to catch an exception, add a new message, and re-throw the new exception while still passing the original exception.

Pang
  • 9,564
  • 146
  • 81
  • 122
burnttoast11
  • 1,164
  • 16
  • 33
  • 2
    You can define your own exceptions in javascript: http://stackoverflow.com/a/464500/1291428 . Then, the concept is somewhat a global standard: the exception should be appropriate to whom is consuming your api. So, it means catching it if necessary, adding following information, logging the original exception etc. Refer to Joshua Bloch, Effective Java 2nd Edition, Item 61: Throw exceptions appropriate to the abstraction – Sebas Oct 29 '14 at 20:28
  • I see. I thought there was maybe a way this is typically handled to make it easy for the consumer of the API. I'll probably just add a field called innerException to a custom exception. Thanks! – burnttoast11 Oct 29 '14 at 20:33
  • throw reveals a stack, so you should be able to trace exactly where the problem lays. that said, stack is not a standard property, though it is, in a few slightly different formats, widely-implimented. – dandavis Oct 29 '14 at 20:48

2 Answers2

9

You can throw any object you want:

try {
    var x = 1/0; 
}
catch (e) {
    throw new MyException("There is no joy in Mudville", e);
}

function MyException(text, internal_exception) {
    this.text = text;
    this.internal_exception = internal_exception;
}

Then an error will be thrown of type MyException with properties text and internal_exception.

  • I do it like this with the addition that besides "text" and "internalError" I also have a dynamic property "data", an object where I log various info, sometimes related to the input that generated the error. I do it like that for easier and more detailed debugging. – Vee6 Apr 09 '18 at 19:53
  • 3
    Throwing _any_ object is a bad idea though because (to my knowledge) only the `Error` type can be used to _correctly_ get the `stack`/`stackTrace` at the point of the first-throw. So rather than having `function MyException` instead have `class MyError extends Error`. – Dai Sep 11 '21 at 04:53
0

An inner error may be available in the Error.cause property. You may provide one via the options parameter of the Error constructor. Example:

try {
  divide(dividend, divisor);
} catch (err) {
  throw new Error(`Devision by ${divisor} failed. See cause for details.`, { cause: err });
}

When such an error is thrown and printed, it will show the inner errors recursivly, just as you'd expect. Running throw new Error("Outer", { cause: new Error("Inner") }); in ts-node REPL produces:

Uncaught Error: Outer
    at <repl>.ts:1:7
    at Script.runInThisContext (node:vm:129:12)
    ... 7 lines matching cause stack trace ...
    at bound (node:domain:433:15) {
  [cause]: Error: Inner
      at <repl>.ts:1:35
      at Script.runInThisContext (node:vm:129:12)
      at runInContext (/usr/local/lib/node_modules/ts-node/src/repl.ts:665:19)
      at Object.execCommand (/usr/local/lib/node_modules/ts-node/src/repl.ts:631:28)
      at /usr/local/lib/node_modules/ts-node/src/repl.ts:653:47
      at Array.reduce (<anonymous>)
      at appendCompileAndEvalInput (/usr/local/lib/node_modules/ts-node/src/repl.ts:653:23)
      at evalCodeInternal (/usr/local/lib/node_modules/ts-node/src/repl.ts:221:12)
      at REPLServer.nodeEval (/usr/local/lib/node_modules/ts-node/src/repl.ts:243:26)
      at bound (node:domain:433:15)

Note that it's only a convention to put the inner error inside cause (but a strong one, as seen from ts-node truncating the outer stack trace due to 7 lines matching cause stack trace). So anything may be inside the cause property, so check it before you consume it! MDN gives an example of putting additional data after the example of using it for an inner error:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause

Allon Guralnek
  • 15,813
  • 6
  • 60
  • 93