Mongoose: Version Error: no matching doc found

Created on 21 Dec 2013  路  21Comments  路  Source: Automattic/mongoose

I have an issue - not sure if I am doing something wrong or it's a bug. I have some products - each of these has an array of variations. I want to go through some data and load it in these variations but I experience a number of 'VersionError: No matching document found' errors.

Thinking I was having a race condition (I am sequentially saving the same document for each of its variations that I modify) I used asyc.eachSeries() but that did not help. Loading the error causing documents one at the time does not yield the error so it seems related to some race condition but I cannot track it down.

Schema:

var Product = new Schema({
  title: {
    type: String,
  },  
  variations: {
    type: Array
  }
});

Sample code:

// Some data to load - the 'variant' is the index of the variations array above
var records = [{
  code: 'foo',
  id: '50ba9c647abe1789f7000073',
  variant: 0
}, {
  code: 'bar',
  id: '50ba9c647abe1789f7000073',
  variant: 1
}, {
  code: 'foobar',
  id: '50ba9c647abe1789f7000073',
  variant: 2
}];

var iterator = function(item, cb) {
  Product.findById(item.id).exec(function(err, product) {
    if(err) {
      return cb(err);
    }
    if (product) {
      product.variations[item.variant].code = item.code.trim();
      product.markModified('variations');
      product.save(function(err, p) {
        return cb(err);
      });
    } else {
      return cb('Missing product');
    }
  });
};

async.eachSeries(records, iterator, function(err) {
  process.exit(1);
});

Most helpful comment

Hi!
I have some problem too!
I noticed that usage of logging modifiedPaths leads to different output data.

First saving:
['Groups']

More saving:
['ModifiedAt', '__v', 'groups']

Perhaps the problem is that the new requires do not use current version __v field (they use remaining in "client" memory).

_SOLVED_
I deleted version property from requests for update.
delete req.body.__v;

All 21 comments

+1

Did you manage to resolve the issue?

Sort of - please see the SO post http://stackoverflow.com/questions/20723474/version-error-on-saving-mongoose-docs - I am not sure why async doesn't work but you can see my original try in the post and in the comments I mention what I did. Essentially I think it's a race condition that you have to deal with all the subdocs at once then save the parent rather than addressing the subdocs one at the time and saving. Still not sure why the above doesn't work though...

Reproduced this bug as well

@niftylettuce I can't quite repro, the following code works fine for me with node 0.10.30 and mongoose 3.8.1 + 3.8.20:

var async = require('async');
var mongoose = require('mongoose');
var product = new mongoose.Schema({
  title: {
    type: String,
  },
  variations: {
    type: Array
  }
});

//Boilerplate
var db = mongoose.connection;
db.on('error', function(err) {
    console.log('Mongoose connection error: ' + err);
});

//connect
mongoose.connect("localhost");

var Product = mongoose.model('gh-1844', product, 'gh-1844');

var x = new Product({ _id: '50ba9c647abe1789f7000073', title: 'bacon', variations: [{ code: 'a' }, { code: 'b' }, {code:'c'}] });
Product.remove({}, function(err) {
  x.save(function(err, x) {
    // Some data to load - the 'variant' is the index of the variations array above
    var records = [{
      code: 'foo',
      id: '50ba9c647abe1789f7000073',
      variant: 0
    }, { 
      code: 'bar',
      id: '50ba9c647abe1789f7000073',
      variant: 1
    }, {
      code: 'foobar',
      id: '50ba9c647abe1789f7000073',
      variant: 2
    }];

    var iterator = function(item, cb) {
      Product.findById(item.id).exec(function(err, product) {
        if(err) {
          return cb(err);
        }
        if (product) {
          console.log(JSON.stringify(product));
          product.variations[item.variant].code = item.code.trim();
          product.markModified('variations');
          product.save(function(err, p) {
            return cb(err);
          });
        } else {
          return cb('Missing product');
        }
      });
    };

    async.eachSeries(records, iterator, function(err) {
      console.log('final err: ' + JSON.stringify(err));
      process.exit(1);
    });
});
});

No version errors. Can you provide an example that shows your issue?

Hi!
I have some problem too!
I noticed that usage of logging modifiedPaths leads to different output data.

First saving:
['Groups']

More saving:
['ModifiedAt', '__v', 'groups']

Perhaps the problem is that the new requires do not use current version __v field (they use remaining in "client" memory).

_SOLVED_
I deleted version property from requests for update.
delete req.body.__v;

Also happens for me, I think it started when upgrading from Mongoose 3.8.14 to 3.8.27.

I was observing this when saving a device twice in rapid succession (but NOT concurrently) with Mongoose 3.8.21.

My issue was that I was copying another Mongoose doc in its entirety, and so I was attempting to update the __v version field to an invalid value.

I'm still having the issue even though i'm not concurrently saving the document.

@raistlinthewiz provide code sample please.

Currently having the same issue on v4.2.5

findBy -> edit document -> save -> versionerror

Code sample please

@vkarpov15
I'm having this issue. I will try to represent the issue as follow.

Model A
name : string,
field1: string,
field2: string,
arrB: array of B

Model B
name : string,
arrC: array of C

Model C
name

These are few API requests performed.
First request: 
    Create instance of A in database with only name (call it a1)
Second request: 
    Create instance of B in database with name (call it b1)
    Find A's previously created instance by id (call it a1)
        push b1 into arrB of a1
        save updated a1
third request:
    Create instance of C in database with name (call it c1)
    Find B's previously created instance by id (call it b1)
        push c1 into arrC of b1
        save updated b1
Fourth request: 
    find a1 by id
    update field1 and field2 of a1
    save the updated a1 instance (this throws error "VersionError" with message: "No matching document found for id "XXc578b3de41826982cXXXXX"")

I hope I'm clear enough to represent the issue.

Code in JavaScript please, not pseudocode, unless there's a well-defined transpiler from pseudocode to JavaScript :palm_tree:

@vkarpov15 Ah, sorry for that. BTW, I solved(managed to avoid) the issue by only updating field1 and field2 in Fourth request.
Alternatively, In javascript

A.findById(req.params.id)
  .populate('arrB')
  .exec(function (err, a1) {
    if (err) { return handleError(res, err); }
    if(!a1) { return res.status(404).send('Not Found'); }
    a1.arrB.forEach(function(b) {
      var targetB = _.find(req.body.arrB, { _id: b._id });
      // update subdocument
      if(targetB) {
        b.name = targetB.name;
        b.save();
      }
    });
    // Commented this line and added 2 lines below to solve/avoid the issue
    // var updated = _.merge(a1, req.body);
    a1.field1 = req.body.field1;
    a1.field2 = req.body.field2;

    a1.save(function (err) {
      if (err) { return handleError(res, err); }
      return res.send(a1);
    });
  });

Im not sure if I solved the issue OR just avoided it. I would appreciate if someone gives suggestion.

Yeah if you do an indiscriminate _.merge() you might set the __v property, which is what versioning uses.

As a side note, you don't need to do the b.save() in the below code:

if(targetB) {
        b.name = targetB.name;
        b.save();
      }

a1.save() should save everything under a1, including the arrB array.

Thats Interesting !! I'll give it a try for sure 馃憤

@vkarpov15 I think I was able to reproduce the issue.

Update

but now i realized products are being removed.
let me try again without removing them to see if still happens.

Update 2

Ok, now we get version error

script.js

var async = require('async');
var mongoose = require('mongoose');
var product = new mongoose.Schema({
  title: {
    type: String,
  },
  variations: {
    type: Array
  }
});

//Boilerplate
var db = mongoose.connection;
db.on('error', function(err) {
    console.log('Mongoose connection error: ' + err);
});

//connect
mongoose.connect('localhost');

var Product = mongoose.model('gh-1844', product, 'gh-1844');

// make sure you have the product already created on the db
//var x = new Product({ _id: '50ba9c647abe1789f7000073', title: 'bacon', variations: [{ code: 'a' }, { code: 'b' }, {code:'c'}] });
//Product.remove({}, function(err) {
  //x.save(function(err, x) {
    // Some data to load - the 'variant' is the index of the variations array above
    var records = [{
      code: 'foo',
      id: '50ba9c647abe1789f7000073',
      variant: 0
    }, { 
      code: 'bar',
      id: '50ba9c647abe1789f7000073',
      variant: 1
    }, {
      code: 'foobar',
      id: '50ba9c647abe1789f7000073',
      variant: 2
    }];

    var iterator = function(item, cb) {
      Product.findById(item.id).exec(function(err, product) {
        if(err) {
          return cb(err);
        }
        if (product) {
          console.log(JSON.stringify(product));
          product.variations[item.variant].code = item.code.trim();
          product.markModified('variations');
          product.save(function(err, p) {
            return cb(err);
          });
        } else {
          return cb('Missing product');
        }
      });
    };

    async.eachSeries(records, iterator, function(err) {
      console.log('final err: ' + JSON.stringify(err));
      process.exit(1);
    });
//});
//});

spawn.js

'use strict';

const spawn = require('child_process').spawn;
const n1 = spawn('node', ['scripts/racecondition']);
const n2 = spawn('node', ['scripts/racecondition']);

n1.stdout.on('data', (data) => {
  console.log(`stdout1: ${data}`);
});

n1.stderr.on('data', (data) => {
  console.log(`stderr1: ${data}`);
});

n1.on('close', (code) => {
  console.log(`child_process1 exited with code ${code}`);
});

n2.stdout.on('data', (data) => {
  console.log(`stdout2: ${data}`);
});

n2.stderr.on('data', (data) => {
  console.log(`stderr2: ${data}`);
});

n2.on('close', (code) => {
  console.log(`child_process2 exited with code ${code}`);
});
$ node spawn

Output

stdout1: con NativeConnection {
  base: 
   Mongoose {
     connections: [ [Circular] ],
     plugins: [],
     models: {},
     modelSchemas: {},
     options: { pluralization: true } },
  collections: {},
  models: {},
  config: { autoIndex: true },
  replica: false,
  hosts: null,
  host: null,
  port: null,
  user: null,
  pass: null,
  name: null,
  options: null,
  otherDbs: [],
  _readyState: 0,
  _closeCalled: false,
  _hasOpened: false,
  _listening: false,
  _events: { error: [Function] },
  _eventsCount: 1 }

stdout2: con NativeConnection {
  base: 
   Mongoose {
     connections: [ [Circular] ],
     plugins: [],
     models: {},
     modelSchemas: {},
     options: { pluralization: true } },
  collections: {},
  models: {},
  config: { autoIndex: true },
  replica: false,
  hosts: null,
  host: null,
  port: null,
  user: null,
  pass: null,
  name: null,
  options: null,
  otherDbs: [],
  _readyState: 0,
  _closeCalled: false,
  _hasOpened: false,
  _listening: false,
  _events: { error: [Function] },
  _eventsCount: 1 }

stdout1: {"_id":"50ba9c647abe1789f7000073","title":"bacon","__v":15,"variations":[{"code":"foo"},{"code":"bar"},{"code":"foobar"}]}

stdout2: {"_id":"50ba9c647abe1789f7000073","title":"bacon","__v":15,"variations":[{"code":"foo"},{"code":"bar"},{"code":"foobar"}]}

stdout1: {"_id":"50ba9c647abe1789f7000073","title":"bacon","__v":16,"variations":[{"code":"foo"},{"code":"bar"},{"code":"foobar"}]}

stdout1: {"_id":"50ba9c647abe1789f7000073","title":"bacon","__v":17,"variations":[{"code":"foo"},{"code":"bar"},{"code":"foobar"}]}

stdout2: final err: {"message":"No matching document found.","name":"VersionError"}

stdout1: final err: null

child_process2 exited with code 1
child_process1 exited with code 1
child process exited with code 1
child process exited with code 1

The thing here is that the portion of the code that updates, could be executed in parallel, for example if you have an endpoint that ultimately ends on this operation, 2 requests could end up on this portion of the code.. and while one does not finishes saving, the other one already found an older document.

does it makes sense?

it is not a bug though, is just they way it is. one solution is trying to save/update in 1 operation instead of find/update

@dciccale after taking a closer look at your code, yep, that's expected behavior and a classic example of how versioning is expected to work - overwriting an array when the array changed underneath you is a version error. You can disable versioning or use the skipVersioning to selectively skip a field for versioning if you're sure you're not worried about conflicting changes.

Hi,
I am getting same issue:

Error
at model.wrappedPointCut [as save] (/home/lt-149/Documents/new/geni-app/admin-end/genican-admin-end/node_modules/mongoose/lib/services/model/applyHooks.js:107:29)
at /home/lt-149/Documents/new/geni-app/admin-end/genican-admin-end/server/admin-routes/fileUpload/index.js:111:66
at Query. (/home/lt-149/Documents/new/geni-app/admin-end/genican-admin-end/node_modules/mongoose/lib/model.js:3411:16)
at /home/lt-149/Documents/new/geni-app/admin-end/genican-admin-end/node_modules/kareem/index.js:264:21
at /home/lt-149/Documents/new/geni-app/admin-end/genican-admin-end/node_modules/kareem/index.js:127:16
at _combinedTickCallback (internal/process/next_tick.js:67:7)
at process._tickCallback (internal/process/next_tick.js:98:9)
message: 'No matching document found for id "58a5a7ca8fc17f0498674304"',
name: 'VersionError'

code snippet:

if (result && result.length > 0) {
for (var a = 0; a < result.length; a++) {
if (result[a].list) {
for (var i = 0; i < result[a].list.length; i++) {

                                       //product
                                       if (result[a] && result[a].list[i] && result[a].list[i].product) {
                                           for (var j = 0; j < result[a].list[i].product.length; j++) {
                                               console.log('---------1---------', result[a].list[i].product[j]._id);
                                               console.log('++++++++++1++++++++', product._id);

                                               if (result[a].list[i].product[j]._id == product._id) {
                                                   result[a].list[i].product[j].coupons = allCoupons;
                                                   if (result[a].list[i].product[j].coupons.length == 0) {
                                                       result[a].list[i].product[j].isCouponAvailable = false;
                                                   } else {
                                                       result[a].list[i].product[j].isCouponAvailable = true;
                                                   }
                                                   console.log("result[a].list[i].product[j]",result[a].list[i].product[j])
                                                   result[a].save(function (err) {
                                                       if (err) {
                                                           console.log("errproduct",err);
                                                  reject();
                                                       }
                                                   });
                                               }
                                           }
                                       }

}
}
}
}

Any help is appreciated.

v5.0.8 exists this problem

Hi, everyone. As i could understand, this error is caused by divergence of _v field of a document, it is not a bug of mongoose itself.

Consider following code:
return await Promise.all(personCompanyUniqueInteractions.map(i => updateMarketingConsent(i)));
_It updates documents in parallel._
The error situtation may happen when in db:
{_id: 'some_id', _v: 3},
but in memory for next update the query is:
{_id: 'some_id', _v: 2, name: 'New name'}

To repair this behavior you may change the code to following:
for (let i = 0; i < personCompanyUniqueInteractions.length; i++) await updateMarketingConsent(personCompanyUniqueInteractions[i]);
_With this approach documents will be updated in a sequence._
Or, alternatively, as @vkarpov15 suggested you can disable versioning or skip versioning.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

varunjayaraman picture varunjayaraman  路  3Comments

simonxca picture simonxca  路  3Comments

Igorpollo picture Igorpollo  路  3Comments

lukasz-zak picture lukasz-zak  路  3Comments

weisjohn picture weisjohn  路  3Comments