最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

JavaScriptTypeScript : Standard way to keep the cause of an Error - Stack Overflow

programmeradmin1浏览0评论

I have a background of Java developer and I'm quite new to JavaScript/TypeScript.

Is there a standard way to manage and keep the cause of an Error in JavaScript/TypeScript?

My purpose is to get a complete stacktrace when I wrap an Error into another ; a little bit as with Java Exception stacktrace:

Message of exception 1
...
Caused by: Message of exception 2
...
Caused by: Message of the root exception

I try this code but err1 doesn't keep the reference of err2:

// CODE
try {
    try {
        throw new Error("Error no2");
    } catch (err2) {
        console.log("========================================");
        console.log(err2);
        throw new Error("Error no1");
    }
} catch (err1) {
    console.log("========================================");
    console.log(err1);
}
// CONSOLE OUTPUT
$ node test.ts 
========================================
Error: Error no2
    at Object.<anonymous> (/tmp/test.ts:3:15)
    at Module._compile (internal/modules/cjs/loader.js:956:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
    at Module.load (internal/modules/cjs/loader.js:812:32)
    at Function.Module._load (internal/modules/cjs/loader.js:724:14)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
    at internal/main/run_main_module.js:17:11
========================================
Error: Error no1
    at Object.<anonymous> (/tmp/test.ts:7:15)
    at Module._compile (internal/modules/cjs/loader.js:956:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
    at Module.load (internal/modules/cjs/loader.js:812:32)
    at Function.Module._load (internal/modules/cjs/loader.js:724:14)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
    at internal/main/run_main_module.js:17:11

Besides, I don't find any property that is named cause in Error class. There is a stack property but I think it's a bad practice to change it.

Thanks!

I have a background of Java developer and I'm quite new to JavaScript/TypeScript.

Is there a standard way to manage and keep the cause of an Error in JavaScript/TypeScript?

My purpose is to get a complete stacktrace when I wrap an Error into another ; a little bit as with Java Exception stacktrace:

Message of exception 1
...
Caused by: Message of exception 2
...
Caused by: Message of the root exception

I try this code but err1 doesn't keep the reference of err2:

// CODE
try {
    try {
        throw new Error("Error no2");
    } catch (err2) {
        console.log("========================================");
        console.log(err2);
        throw new Error("Error no1");
    }
} catch (err1) {
    console.log("========================================");
    console.log(err1);
}
// CONSOLE OUTPUT
$ node test.ts 
========================================
Error: Error no2
    at Object.<anonymous> (/tmp/test.ts:3:15)
    at Module._compile (internal/modules/cjs/loader.js:956:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
    at Module.load (internal/modules/cjs/loader.js:812:32)
    at Function.Module._load (internal/modules/cjs/loader.js:724:14)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
    at internal/main/run_main_module.js:17:11
========================================
Error: Error no1
    at Object.<anonymous> (/tmp/test.ts:7:15)
    at Module._compile (internal/modules/cjs/loader.js:956:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
    at Module.load (internal/modules/cjs/loader.js:812:32)
    at Function.Module._load (internal/modules/cjs/loader.js:724:14)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
    at internal/main/run_main_module.js:17:11

Besides, I don't find any property that is named cause in Error class. There is a stack property but I think it's a bad practice to change it.

Thanks!

Share Improve this question asked Mar 2, 2020 at 15:26 SylmarchSylmarch 4832 gold badges4 silver badges7 bronze badges 5
  • In Java, it's pretty much a standard practice to wrap an exception in another and re-throw it, e.g., catch(IOException e) { throw new CannotReadDiskException(e)}. However, in JS it's quite unusual to catch an error and re-throw it. You either deal with it or just let it propagate. What you're looking for seems to be a custom error type, I suppose. You can also just throw any values, not just Error objects - it's another option of handling this. – VLAZ Commented Mar 2, 2020 at 15:40
  • Although, I should note that the there is no standard mandating an Error has a stack property. It's perfectly valid for the code to just throw errors that only contain a message, without pointing to where they came from. Not sure if any environments do that but still - when talking a standard way to capture and preserve stack traces, there isn't any, to my knowledge, since there is no standard for stack traces. – VLAZ Commented Mar 2, 2020 at 15:43
  • I'm not sure I understand--the snippet you show and the results are identical to what would happen in Java. I've modified the stack trace for "nested" exceptions, or added on, but that's about it. – Dave Newton Commented Mar 2, 2020 at 15:49
  • @DaveNewton in Java, you'd receive one Exception object which can link to its cause, which can then link to another cause, etc. When dumping the stacktrace, you'd get something that looks similar but you'd actually just be logging the last exception. The code sample here logs each Error as it's caught. So, it's not exactly the same as Java. Example - in Java, you can catch IllegalArgumentException and re-throw as a custom InvalidUserInputException, then later on catch that one and perhaps handle it somehow without logging it. In JS you either log the first or both, no way to suppress it. – VLAZ Commented Mar 2, 2020 at 16:03
  • @VLAZ The example shown catches an exception and throws another one; there would be no connection between them unless the first is provided as an argument to the second (in Java), which is why the example is working as expected, and would be the same in JS or Java. – Dave Newton Commented Mar 2, 2020 at 16:09
Add a comment  | 

3 Answers 3

Reset to default 11

2021 answer: There is now a standard way of managing and keeping the cause an error. The Error() constructor can now accept an options object as a second argument. This options object should have a cause property which would typically be another Error but it doesn't have to be.

Example

throw new Error('Outer Error', {cause: new Error('Inner Error')});

Support

At the time of writing (October 2021) there's support for the new constructor in Firefox, Chrome and Safari. To get the cause of an Error you'll need to interrogate it's cause property. Additionally in Firefox we get a bit of extra info in the console, e.g

Uncaught Error: Outer Error
    <anonymous> debugger eval code:1
Caused by: Error: Inner Error
    <anonymous> debugger eval code:1

Edge will currently just ignore the second constructor property.

Sources

Mozilla doc: https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/error

ECMAScript proposal: https://github.com/tc39/proposal-error-cause

I've always used a modification of Kristian's answer here

class TraceableError extends Error {
  trace: Error;

  constructor(message?: string, innerError?: Error) {
    super(message); 
    this.trace = innerError;

    const actualProto = new.target.prototype;

    if (Object.setPrototypeOf) { Object.setPrototypeOf(this, actualProto); } 
    else { this.__proto__ = actualProto; } 
  }
}

Then you would throw like this

try {
    try {
        throw new Error("Error no2");
    } catch (err2) {
        console.log("========================================");
        console.log(err2);
        throw new TraceableError ("Error no1", err2);
    }
} catch (err1) {
    console.log("========================================");
    console.log(err1);
}

Note that, if not caught, the trace part of the new error will not be output to the console.

I use the Error cause option a lot. My text building function that the loggers use looks like this

export function getErrorMessageWithCausedByParts(err: Error, stack=true): string {
  const doNewLines = stack;
  let txt = "";
  
  if(stack) {
    txt+=err.stack;
  } else {
    txt+=err.message;
  }

  if(err.cause != null) {
    if (err.cause instanceof Error) {
      if(doNewLines) {
        txt+="\n[caused by] ";
      } else {
        txt += " [caused by] ";
      }
      
      txt += getErrorMessageWithCausedByParts(err.cause, stack);
    } else {
      txt += `[Unknown cause type?!]: ${JSON.stringify(err.cause)}`
    }
  }

  return txt;
}

Note: to be able to use the cause option of the Error object you have to set a newer ES version in your tsconfig.json

"compilerOptions": {

    "lib": ["es2022"]

    ...
}
发布评论

评论列表(0)

  1. 暂无评论