Why are my JS promise catch error objects empty?

16,468

Solution 1

Problem:

Logging the error object to console by itself will reveal that the object is indeed not empty and grabbing properties such as err.message is very doable.

The problem is that JS Error object cannot be naively stringified using JSON. This - along with ways to deal with this problem - are described in detail in the associated SOF Question: Is it not possible to stringify an Error using JSON.stringify?.

Updated code:

The following code changes were made to specify the message be shown in the HTTP response and the helper function (previously described) has been updated to specific the details of the error to be shown: Ex: { err: { message: err.message, stack: err.stack } } and so on.

    var console = clim("(DELETE /api/v1/team/:team/comments/:comment):", logger);

    // create a filters request for mongoose
    var query = {};

    // determine if the :team param is a username or an object id
    req.helpers.validateObjectId(req.db, req.params.team) ? query._id = req.params.team : query.name = req.params.team;

    if(req.helpers.validateObjectId(req.db, req.params.comment)) {

        // looks good; create an update object
        var update = { $pull: { comments: { _id: req.params.comment } } };

        // find the comment using the query above and pull the comment id
        req.models.Team.findOneAndUpdate(
            query,
            update,
            {safe: true, new : true}
        ).then(function(team){

            if(!team){

                // create the response object
                var response = {
                    success: false,
                    message: "Team not found"
                };

                // log request
                console.info(req.helpers.consoleMessage(req, response, null));

                // respond with an appropriate array
                res.status(404).json(response);

            }else{

                // create the response object using the teams's comments
                var response = team.comments;

                // log request
                console.info(req.helpers.consoleMessage(req, response, null));

                // respond with the team comments array
                res.status(200).json(response);

            }

        }).then(null, function(err){

            // create the response
            var response = { success: false, message: req.config.debug ? err.message : "An error has occur with your request; please try again" };

            // log the errors
            console.error(req.helpers.consoleMessage(req, response, err));

            // or send a 500 internal server error
            res.status(500).json(response);

        });

    }else{

        // create the response
        var response = { success: false, message: "Comment id is not a valid object id" };

        // log the errors
        console.info(req.helpers.consoleMessage(req, response, null));

        // or send a 500 internal server error
        res.status(500).json(response);

    }

Why am I sharing such a simple concept?

I searched for hours trying to figure out what I was doing incorrectly with my promise chain structures and used the keywords empty and error (along with every combination of words regarding JS Promises) but none of my searches came up with anything useful other than confirming that I was approaching this correctly. Everything appeared to be fine with my helper script and I couldn't seem to do the right debugging steps to figure out where the problem was until I happened upon trying to output the err object directly into the console (why would I need to do this? I already was... or so I thought).

So I guess you could say I'm trying to save some folks some time in case anyone runs into a similar situation and is thinking "why are my promises not working as intended!" and, like me, happen to be searching in the incorrect direction.

Happy Coding!

Solution 2

The problem with an empty object occurs when you trying to use JSON.stringify

const error = new Error('Oops');
const output = JSON.stringify(error);
console.log(output); // {}
const error = new Error('Oops');
const output = JSON.stringify(error.message); // <--- Use error.message to fix that
console.log(output); // "Oops"

Solution 3

For whoever wondering why the throw error() is just an empty object, it is because you need to log it as error.message accessing it message property.

Share:
16,468
ccampanale
Author by

ccampanale

I'm a senior DevOps engineer working for GDIT on a contract for NIAID. I am part of a team of engineers who build and support a custom platform for IT services delivery. We integrate several tools and services to provide a secure, cloud-based, infrastructure and application deployments incorporating infrastructure as code and providing CI/CD tools and practices for our users. I'm personally responsible for leading a small part of our team responsible for the web-services side of our operations; I currently spend most of my time architecting and writing new and expanded microservices in Javascript (NodeJS). Additionally, a large part of what we do is customer outreach; our team hosts monthly webinars to discuss methods and practices both within our platform as well the industry.

Updated on June 26, 2022

Comments

  • ccampanale
    ccampanale almost 2 years

    Preface:

    • Using Mongoose
    • Using Bluebird and replacing mpromise inside of Mongoose
    • The req.helpers.consoleMessage function seen bellow is a function with some simple logic in it that determines when to and not to display a certain level of detail based the existent of debug being turned on in the app configuration AND the non-null/undefined state of the objects being displayed. The entire messages gets stringified using JSON and returned to be displayed on the console.

    Code:

    Here is an example of some code showing these symptoms.

    This is a delete route for the:team :comment units in an API I'm working on. I have intentionally left the line var response = user.comments; with an error in it referencing to a user object when in fact it should be team which should be returned by the calling function and passed into the Promise.then(). This should cause a reference error as user is not defined.

        var console = clim("(DELETE /api/v1/team/:team/comments/:comment):", logger);
    
        // create a filters request for mongoose
        var query = {};
    
        // determine if the :team param is a username or an object id
        req.helpers.validateObjectId(req.db, req.params.team) ? query._id = req.params.team : query.name = req.params.team;
    
        if(req.helpers.validateObjectId(req.db, req.params.comment)) {
    
            // looks good; create an update object
            var update = { $pull: { comments: { _id: req.params.comment } } };
    
            // find the comment using the query above and pull the comment id
            req.models.Team.findOneAndUpdate(
                query,
                update,
                {safe: true, new : true}
            ).then(function(team){
    
                if(!team){
    
                    // create the response object
                    var response = {
                        success: false,
                        message: "Team not found"
                    };
    
                    // log request
                    console.info(req.helpers.consoleMessage(req, response, null));
    
                    // respond with an appropriate array
                    res.status(404).json(response);
    
                }else{
    
                    // create the response object using the teams's comments
                    var response = user.comments;
    
                    // log request
                    console.info(req.helpers.consoleMessage(req, response, null));
    
                    // respond with the team comments array
                    res.status(200).json(response);
    
                }
    
            }).then(null, function(err){
    
                // create the response
                var response = { success: false, message: req.config.debug ? err: "An error has occur with your request; please try again" };
    
                // log the errors
                console.error(req.helpers.consoleMessage(req, response, err));
    
                // or send a 500 internal server error
                res.status(500).json(response);
    
            });
    
        }else{
    
            // create the response
            var response = { success: false, message: "Comment id is not a valid object id" };
    
            // log the errors
            console.info(req.helpers.consoleMessage(req, response, null));
    
            // or send a 500 internal server error
            res.status(500).json(response);
    
        }
    

    Symptom:

    Calling this route will produce an error and cause the .catch() to fire in an attempt to recover from the errored state however the err object appears to be empty.

    Ex. HTTP Response: { success: false, message: {} }

    Ex. Console log (abridged for clarity) { req: {...}, res: {...}. err: {} }

    Conclusion:

    The err object appears to be empty...

  • danilodeveloper
    danilodeveloper almost 8 years
    Cool! Thank you for share.
  • smashedtoatoms
    smashedtoatoms over 6 years
    Hah, I made the exact same mistake. This is the most useful thing on stack overflow today for me. Thanks.
  • Noah Gary
    Noah Gary over 2 years
    Man, this threw me for a loop for the whole day. error.message worked.
  • ccampanale
    ccampanale over 2 years
    This is unfortunately very misleading and mostly incorrect. Why the object is empty is explained above and in more detail in the provided link; your guidance doesn't explain why the object is empty and simply instructs someone to only use one of the properties of that object. There are other important parts of a native Error object, such as the stack trace.
  • Mel Macaluso
    Mel Macaluso over 2 years
    Acknowledged. I guess most of the time when you want to access the error in the FE it would be to analyse the error.code if you have a custom error handler based on that and / or message to present it to the end user, the error stack trace would be for debugging.