diff --git a/lib/schema/bigint.js b/lib/schema/bigint.js index f6619a19e35..02912627dd5 100644 --- a/lib/schema/bigint.js +++ b/lib/schema/bigint.js @@ -212,7 +212,14 @@ SchemaBigInt.prototype.castForQuery = function($conditional, val, context) { return this.applySetters(null, val, context); } - return this.applySetters(val, context); + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } }; /** diff --git a/lib/schema/boolean.js b/lib/schema/boolean.js index 1dc67e2aaa5..a887a9e07e9 100644 --- a/lib/schema/boolean.js +++ b/lib/schema/boolean.js @@ -256,7 +256,14 @@ SchemaBoolean.prototype.castForQuery = function($conditional, val, context) { return this.applySetters(null, val, context); } - return this.applySetters(val, context); + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } }; /** diff --git a/lib/schema/buffer.js b/lib/schema/buffer.js index 655ccc2c9f6..6444ebd8835 100644 --- a/lib/schema/buffer.js +++ b/lib/schema/buffer.js @@ -279,7 +279,16 @@ SchemaBuffer.prototype.castForQuery = function($conditional, val, context) { } return handler.call(this, val); } - const casted = this.applySetters(val, context); + + let casted; + try { + casted = this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } return casted ? casted.toObject({ transform: false, virtuals: false }) : casted; }; diff --git a/lib/schema/date.js b/lib/schema/date.js index 7dcd998922f..6cbfee83865 100644 --- a/lib/schema/date.js +++ b/lib/schema/date.js @@ -407,7 +407,14 @@ SchemaDate.prototype.$conditionalHandlers = { SchemaDate.prototype.castForQuery = function($conditional, val, context) { if ($conditional == null) { - return this.applySetters(val, context); + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } } const handler = this.$conditionalHandlers[$conditional]; diff --git a/lib/schema/number.js b/lib/schema/number.js index 432acd2aca7..d89ab7d63c0 100644 --- a/lib/schema/number.js +++ b/lib/schema/number.js @@ -429,7 +429,16 @@ SchemaNumber.prototype.castForQuery = function($conditional, val, context) { } return handler.call(this, val, context); } - val = this.applySetters(val, context); + + try { + val = this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } + return val; }; diff --git a/lib/schema/string.js b/lib/schema/string.js index 2e39baac733..e7eed1c4f03 100644 --- a/lib/schema/string.js +++ b/lib/schema/string.js @@ -688,7 +688,14 @@ SchemaString.prototype.castForQuery = function($conditional, val, context) { return val; } - return this.applySetters(val, context); + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } }; /*! diff --git a/lib/schema/uuid.js b/lib/schema/uuid.js index 41ab1f60e49..aa72c42107f 100644 --- a/lib/schema/uuid.js +++ b/lib/schema/uuid.js @@ -344,8 +344,15 @@ SchemaUUID.prototype.castForQuery = function($conditional, val, context) { if (!handler) throw new Error('Can\'t use ' + $conditional + ' with UUID.'); return handler.call(this, val, context); - } else { - return this.cast(val); + } + + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; } }; diff --git a/lib/schemaType.js b/lib/schemaType.js index ee8c9a6aeeb..25e0c0b4de8 100644 --- a/lib/schemaType.js +++ b/lib/schemaType.js @@ -1630,7 +1630,14 @@ SchemaType.prototype.castForQuery = function($conditional, val, context) { return handler.call(this, val, context); } - return this.applySetters(val, context); + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } }; /** diff --git a/test/model.findOneAndUpdate.test.js b/test/model.findOneAndUpdate.test.js index 6296b529ffb..56ac556eb6c 100644 --- a/test/model.findOneAndUpdate.test.js +++ b/test/model.findOneAndUpdate.test.js @@ -2200,4 +2200,34 @@ describe('model: findOneAndUpdate:', function() { ); assert.equal(updated.defaultField, 'some non-default value'); }); + + it('sets CastError path to full path (gh-14114)', async function() { + const testSchema = new mongoose.Schema({ + id: mongoose.Schema.Types.ObjectId, + name: String, + accessories: [ + { + isEnabled: Boolean, + additionals: [ + { + k: String, + v: Number + } + ] + } + ] + }); + const Test = db.model('Test', testSchema); + const err = await Test.findOneAndUpdate( + {}, + { + $set: { + 'accessories.0.additionals.0.k': ['test'] + } + } + ).then(() => null, err => err); + assert.ok(err); + assert.equal(err.name, 'CastError'); + assert.equal(err.path, 'accessories.0.additionals.0.k'); + }); }); diff --git a/test/schema.select.test.js b/test/schema.select.test.js index 70631978c76..1b739978a4f 100644 --- a/test/schema.select.test.js +++ b/test/schema.select.test.js @@ -15,10 +15,12 @@ describe('schema select option', function() { before(function() { db = start(); + mongoose.set('debug', true); }); after(async function() { await db.close(); + mongoose.set('debug', false); }); beforeEach(() => db.deleteModel(/.*/)); @@ -53,7 +55,7 @@ describe('schema select option', function() { assert.equal(findByIdDocAgain.isSelected('name'), false); assert.equal(findByIdDocAgain.isSelected('docs.name'), false); assert.strictEqual(undefined, findByIdDocAgain.name); - const findUpdateDoc = await Test.findOneAndUpdate({ _id: doc._id }); + const findUpdateDoc = await Test.findOneAndUpdate({ _id: doc._id }, { name: 'the excluded' }); assert.equal(findUpdateDoc.isSelected('name'), false); assert.equal(findUpdateDoc.isSelected('docs.name'), false); assert.strictEqual(undefined, findUpdateDoc.name);