mongoose: Throw clean error if save() called in parallel on same doc instance

I have seen a few issues closed already regarding the #save() method and swallowing of exceptions. I’ve got this happening at the moment with the latest version in npm (4.4.12). Here’s some code:

const giftcard = new Giftcard({ // new instance of the model
  // setting some properties here
});

giftcard.save((err) => {
  if (err) throw new Error(err);
  // this never happens - the callback is never called
});

I can post a more detailed example if required.

Edit: Also, this happens with the promise interface as well, so giftcard.save().then() has the same result.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 8
  • Comments: 61 (13 by maintainers)

Commits related to this issue

Most upvoted comments

@mtimofiiv @cwhenderson20 @dodekeract I have just donated over PayPal to @vkarpov15 (val@karpov.io) for his time helping me debug this issue. If you have been able to get passed this through our findings please send him some dough! 💸 He deserves it! He spent a ton of time with me, even while he was on his mobile phone he was helping!

@vkarpov15 I am experiencing this same issue on version 4.4.20. Attempting to call save on my model results in the callback never being called while running the same operation with $__save works just fine.

I am not overwriting promises and I do not have any pre-save (or otherwise) middleware for the model and using mongoose.set("debug", true) does not show anything strange. I have also verified that the document I’m attempting to save does pass validation.

I have additionally attached a connection error handler to the mongoose instance and no errors are reported.

Example:

const appt = new Appointment();
// fill with data here

appt.save((err, doc) => {
    // callback never called
});

const appt2 = new Appointment();
// fill with data here

appt2.$__save({}, (err, doc) => {
    // callback is called
});

For now I will use the private method as I’m not sure of how to proceed otherwise, but if I can give any more information to help with this, please let me know.

Update Similar to @mtimofiiv, console.log-ing the methods $__save and save give me the actual $__save method as defined in model.js and a function called wrappedPointCut, respectively.

Additionally, after modifying my installed version of mongoose (to log when methods are called) and re-running the code, I can see that indeed the $__save method reports that it is being called, while similar logging for save never reports.

@vkarpov15 and I talked over Google Chat, and we discovered that due to the validator library accepting two arguments for most functions, e.g. my case with isEmail(str, options) and @mtimofiiv case with validator.isURL(str, options), it thinks that the options object is the asynchronous callback, which it isn’t a function, so nothing happens and it hangs.

No, I am not overwriting the promise - I will endeavour to put together a more reproducable case for you.

@vkarpov15 Thanks for the wonderful library. It’s fantastic.

So, very occasionally I get errors like this:

Error
    at model.wrappedPointCut [as save] (/home/project/build/processes/1517989482/node_modules/mongoose/lib/services/model/applyHooks.js:131:29)
    at generator.generate.then.generatedImageUrls (/home/project/build/processes/1517989482/node_modules/@chaddjohnson/services/lib/models/product.js:119:25)
    at <anonymous>
Error
    at model.wrappedPointCut [as save] (/home/project/build/processes/1518143934/node_modules/mongoose/lib/services/model/applyHooks.js:131:29)
    at handleOrderProcessing.then.catch.transactionError (/home/project/build/processes/1518143934/node_modules/@chaddjohnson/services/lib/paymentProcessors/authorizeNet.js:203:34)
    at <anonymous>
    at process._tickDomainCallback (internal/process/next_tick.js:228:7)

When this happens, it happens when I call .save() on a model instance. I have no solid leads as to the cause. The only things I can think of are:

  1. I am using usePushEach for every model.
  2. I sometimes bypass Mongoose .save() to prevent triggering post save hooks, like so:
return new Promise((resolve, reject) => {
    Order.update({_id: order._id, __v: order.__v}, {$set: {shopifyOrderData: data.shopifyOrderData}}, (error) => {
        if (error) {
            return reject(error);
        }

        resolve();
    });
})

this solved my problem… connect using mongo client… http://mongoosejs.com/docs/connections.html#use-mongo-client

This is a dupe of #4084, see http://mongoosejs.com/docs/validation.html#async-custom-validators and https://github.com/Automattic/mongoose/issues/4084#issuecomment-213456512. Since so many people are getting bit by this, I’m adding a quick workaround.

Thanks @dodekeract , I’ll try your code and see if I can repro. A test case would be very helpful 👍