Repository: Automattic/mongoose (JavaScript)

Issue:
Mongoose 8.x: Required array schema validation breaks in static validation
### Prerequisites

- [x] I have written a descriptive issue title
- [x] I have searched existing issues to ensure the bug has not already been reported


### Mongoose version

8.9.3

### Node.js version

20.17.0

### MongoDB server version

7.0.14

### Typescript version (if applicable)

5.5.4

### Description

When using a schema with a required array field, static validation using `Model.validate()` fails when the array is empty, even though instance validation using `new Model().validate()` passes.

### Steps to Reproduce

Run the following code with any 8.x.x version and it will fail the static validation:

```javascript
const mongoose = require('mongoose');

async function runValidationTest() {
    const barSchema = new mongoose.Schema({
        foo: {
            type: mongoose.Schema.Types.String,
            required: true,
        },
    });

    const fooSchema = new mongoose.Schema({
        bars: {
            type: [barSchema],
            required: true,
        },
    });

    const Model = mongoose.model('FooTest', fooSchema);

    const fooInstance = {
        bars: [],
    };

    console.log('--- Instance Validation (new Model().validate()) ---');
    try {
        await new Model(fooInstance).validate();
        console.log('Instance validation: PASSED ✓');
    } catch (error) {
        console.error('Instance validation: FAILED ✗', error);
    }

    console.log('\n--- Static Validation (Model.validate()) ---');
    try {
        await Model.validate(fooInstance);
        console.log('Static validation: PASSED ✓');
    } catch (error) {
        console.error('Static validation: FAILED ✗', error);
    }
}

// Run the test
runValidationTest().catch(console.error);

```

### Expected Behavior

Both instance validation (`new Model().validate()`) and static validation (`Model.validate()`) should pass when the bars array is empty because the array exists and satisfies the required constraint.


Discussion:
Maybe has something in common with this issue: https://github.com/Automattic/mongoose/issues/15075



Patch:
diff --git a/lib/model.js b/lib/model.js
index 67ccfb83a61..1095ef97f19 100644
--- a/lib/model.js
+++ b/lib/model.js
@@ -3704,8 +3704,9 @@ Model.castObject = function castObject(obj, options) {
             Model.castObject.call(schemaType.caster, val)
           ];
         }
+
+        continue;
       }
-      continue;
     }
     if (schemaType.$isSingleNested || schemaType.$isMongooseDocumentArrayElement) {
       try {


Test patch:
diff --git a/test/model.test.js b/test/model.test.js
index e6a1ef9cd3e..af518fe7340 100644
--- a/test/model.test.js
+++ b/test/model.test.js
@@ -7796,6 +7796,29 @@ describe('Model', function() {
       const obj = { sampleArray: { name: 'Taco' } };
       assert.throws(() => Test.castObject(obj), /Tried to set nested object field `sampleArray` to primitive value/);
     });
+    it('handles document arrays (gh-15164)', function() {
+      const barSchema = new mongoose.Schema({
+        foo: {
+          type: mongoose.Schema.Types.String,
+          required: true
+        }
+      }, { _id: false });
+
+      const fooSchema = new mongoose.Schema({
+        bars: {
+          type: [barSchema],
+          required: true
+        }
+      });
+
+      const Test = db.model('Test', fooSchema);
+
+      let obj = Test.castObject({ bars: [] });
+      assert.deepStrictEqual(obj.bars, []);
+
+      obj = Test.castObject({ bars: [{ foo: 'bar' }] });
+      assert.deepStrictEqual(obj.bars, [{ foo: 'bar' }]);
+    });
   });
 
   it('works if passing class that extends Document to `loadClass()` (gh-12254)', async function() {