Trying to remove a subdocument in Mongoose gives me an internal mongoose error

I have a schema as follows (simplified):

var Permission = new Schema({
  _id: String,  // email address
  role: String  // "admin" or "member"
});

var Org = new Schema({
  name: {type: String, index: {unique: true, dropDups: true}, trim: true},
  permissions: [Permission]
});

An example document would look like this:

{
  "name": "My Org",
  "permissions" : [
    {"_id" : "joe@gmail.com", "role" : "admin"},
    {"_id" : "mary@gmail.com", "role" : "member"}
  ]
}

I am trying to delete one of the permissions rows, using the command org.permissions.remove(req.params.email), as shown in context below:

exports.removePermissions = function(req, res) {
  var name = req.params.name;
  return Org
    .findOne({name: name})
    .select()
    .exec(function(err, org) {
      if (err) return Org.handleError(res, err);
      if (!org) return Org.handleError(res, new Error("#notfound " + name));
      org.permissions.remove(req.params.email);
      org.save(function(err, org) {
        if (err) return Org.handleError(res, err);
        else return res.send(org);
      });
    });
};

When I do this, I get the following error:

TypeError: Cannot use 'in' operator to search for '_id' in joe@gmail.com
    at EmbeddedDocument.Document._buildDoc (/../node_modules/mongoose/lib/document.js:162:27)
    at EmbeddedDocument.Document (/../node_modules/mongoose/lib/document.js:67:20)
    at EmbeddedDocument (/../node_modules/mongoose/lib/types/embedded.js:27:12)
    at new EmbeddedDocument (/../node_modules/mongoose/lib/schema/documentarray.js:26:17)
    at MongooseDocumentArray._cast (/../node_modules/mongoose/lib/types/documentarray.js:62:10)
    at Object.map (native)
    at MongooseDocumentArray.MongooseArray.remove (/../node_modules/mongoose/lib/types/array.js:360:21)
    at model.Org.methods.removePermissions (/../models/org.js:159:20)

The only thing I can think of is that Mongoose does not support _id fields that are not ObjectID's? This is strange, because I use these elsewhere in my code and it works fine (e.g. org.permissions.id("joe@gmail.com") works).

Any suggestions much appreciated!

I'm not sure why using remove there isn't working, but you can do this atomically with findOneAndUpdate and the $pull operator:

exports.removePermissions = function(req, res) {
  var name = req.params.name;
  return Org.findOneAndUpdate(
    {name: name}, 
    {$pull: {permissions: {_id: req.params.email}}},
    function(err, org) {
      // org contains the updated doc
      ...
    });
};

As per this answer, you need to call remove() on the subdocument you want to remove, rather than on the entire subdocument array.

So, change:

org.permissions.remove(req.params.email);

to:

org.permissions.id(req.params.email).remove();

This two-step method has the added advantage over the answer supplied by @JohnnyHK in that you can validate whether the subdocument actually exists before removing it. This can be useful if you'd like to send a 404 response indicating that the subdocument doesn't exist - as far as I am aware, this isn't possible using the $pull atomic operator.

Note that this also will only work if your subdocument array has a schema, as illustrated in the question. If it doesn't, or it has a schema type of Mixed, the collection returned from the database will be a plain array rather than a Mongoose-enhanced array. This means that there is no .id() function. In this case, I would use lodash#remove instead:

_.remove(org.permissions, (function(permission) {
  return permission._id.toString() === req.params.email;
}));