33

In my NodeJS program, I parse some user JSON file.

So I use :

this.config = JSON.parse(fs.readFileSync(path));

The problem is that if the json file is not correctly formated, the error thrown is like:

undefined:55
            },
            ^
SyntaxError: Unexpected token }
    at Object.parse (native)
    at new MyApp (/path/to/docker/lib/node_modules/myApp/lib/my-app.js:30:28)
...

As it is not really user friendly I would like to throw an Error specifying some user friendly message (like "your config file is not well formated") but I want to keep the stacktrace in order to point to the problematic line.

In the Java world I used throw new Exception("My user friendly message", catchedException) in order to have the original exception which caused that one.

How is it possible in the JS world?

4
  • Wrap it in try{}catch(){} and create your own message. Commented Sep 10, 2014 at 11:10
  • Yes this is how I wanted to do at first. But I wanted to keep the original stack also, which contains the original message pointing to the problematic line (in my example)... And I can't find a "good" way to do that in the JS error handling I saw until now on the web...
    – Anthony O.
    Commented Sep 10, 2014 at 11:13
  • 1
    I actually see that my question is a duplicate of: stackoverflow.com/questions/17886769/…
    – Anthony O.
    Commented Sep 10, 2014 at 11:44
  • Node.js now supports adding specific cause of the error: stackoverflow.com/questions/1382107/…
    – t_dom93
    Commented Sep 28, 2021 at 16:54

6 Answers 6

39

What I finally did is:

try {
    this.config = JSON.parse(fs.readFileSync(path));
} catch(err) {
    var newErr = new Error('Problem while reading the JSON file');
    newErr.stack += '\nCaused by: '+err.stack;
    throw newErr;
}
4
  • 3
    And yet the "primary source" regarding best practices mentions not to touch the "stack" property at all. In java there's a "cause" property. Do nodejs people expect that to be set?
    – dmansfield
    Commented Sep 14, 2015 at 15:04
  • A better answer would be something that only calculates the stack when it is read. Something like Object.defineProperty(newErr, 'stack', {get: function() { return ??? + '\nCaused by: ' + err.stack } }). But barring that, this seems pretty nice to me.
    – Adam A
    Commented Apr 26, 2016 at 6:28
  • 1
    I assume the last line should be: throw newErr;
    – fmpdmb
    Commented Sep 16, 2016 at 16:55
  • this is what i was looking for thanks. This is a clear example of what to do when you have a custom stack trace and you are throwing an error from a controlled situation (ie when a library provides you a with a stacktrace without an error)
    – Illiax
    Commented May 14, 2018 at 21:17
10

There is an new Error Cause proposal for ECMAScript, and it reached stage-4 at TC34!

It means it will be in the next ECMAScript version!

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

You would provide the cause as an error option:

throw new Error(`Couldn't parse file at path ${filePath}`, { cause: err });

The ES proposal only formalize it on the language level, but browsers/NodeJS should normally agree to log the full causal chain in practice (see https://github.com/nodejs/node/issues/38725)


As of today (end of 2021), Firefox Devtools are already able to log nested stacktraces!

enter image description here

4

Joyent released a Node.js package that can be used exactly for that. It is called VError. I paste an example of how you would use the pacakge:

var fs = require('fs');
var filename = '/nonexistent';
fs.stat(filename, function (err1) {
    var err2 = new VError(err1, 'stat "%s"', filename);
    console.error(err2.message);
});

would print the following:

stat "/nonexistent": ENOENT, stat '/nonexistent'
2
  • 3
    Please don't post identical answers to multiple questions. Post one good answer, then vote/flag to close the other questions as duplicates. If the question is not a duplicate, tailor your answers to the question.
    – Paul Roub
    Commented Oct 27, 2016 at 14:56
  • @netflix/nerror is a more modern alternative to verror and has built-in types. Commented Feb 8, 2021 at 11:18
0

2021 Update: To chain exceptions in JS:

class MyAppError extends Error {
    constructor(...params) {
        super(...params)
        if (Error.captureStackTrace) {
            // This is the key line!
            Error.captureStackTrace(this, this.constructor);
        }
        this.name = this.constructor.name
    }
}

See the Mozilla docs on Error.captureStackTrace

1
  • 4
    This is not standard, it's a specific V8 function. Commented Feb 8, 2021 at 11:16
0
try {
  throw new Error('Original error');
} catch (originalError) {
  throw new Error('New error message', { cause: originalError });
}
2
  • 3
    Should spend some time explaining the code instead of a code dump. Commented Jun 12 at 1:11
  • 1
    Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?
    – Karan
    Commented Jun 12 at 5:54
-4

Use a try / catch block:

try {
    this.config = JSON.parse("}}junkJSON}");
    //...etc
}
catch (e) {
    //console.log(e.message);//the original error message 
    e.message = "Your config file is not well formatted.";//replace with new custom message
    console.error(e);//raise the exception in the console
    //or re-throw it without catching
    throw e;
}

http://jsfiddle.net/0ogf1jxs/5/

UPDATE: If you really feel the need for a custom error you can define your own:

function BadConfig(message) {
   this.message = message;
   this.name = "BadConfig";
}
BadConfig.prototype = new Error();
BadConfig.prototype.constructor = BadConfig;

try {
    this.config = JSON.parse("}}badJson}");
} catch(e) {
    throw new BadConfig("Your JSON is wack!");
}

http://jsfiddle.net/kL394boo/

Lots of useful info at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error

6
  • I wanted to "throw" something because I'm in library code, so I want the developer who uses it to be able to catch it and display it to the user in the way he wants... Actually I want to do something like passing the root cause to a new Exception in Java
    – Anthony O.
    Commented Sep 10, 2014 at 11:21
  • See: stackoverflow.com/questions/783818/…
    – Moob
    Commented Sep 10, 2014 at 11:29
  • 1
    Just edit this one and change e.message to your user friendly message and throw e Commented Sep 10, 2014 at 11:44
  • @JonasLiljestrand - Updated answer accordingly
    – Moob
    Commented Sep 10, 2014 at 11:53
  • 1
    Thank you for your answer but it still doesn't reply to my question which is: how to chain exceptions in JS like adding cause to an Exception in Java. Your BadConfig class doesn't include the catched e and can't display it as a cause in the error log if the one who catch the BadConfig output it in console.error.
    – Anthony O.
    Commented Sep 10, 2014 at 13:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.