Skip to content

Mongoose 8.x: Discriminator schema validation breaks in static validation #15075

@skrtheboss

Description

@skrtheboss

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

8.8.3 (also observed in 8.0.0)

Node.js version

20.17.0

MongoDB server version

7.0.15

Typescript version (if applicable)

No response

Description

There are significant inconsistencies in validation behavior between instance validation (new Model().validate()) and static validation (Model.validate()) when using discriminator schemas with nested document arrays:

Instance Validation

  • Circle: Passes validation ✓
  • Square: Fails validation as expected ✓

Static Validation

  • Circle: Fails validation with "radius is required" ✗
  • Square: Passes validation unexpectedly ✗

Regression?

This issue appears to have changed in behavior between Mongoose versions:

  • Mongoose 6.13.5: Both instance and static validation work consistently
  • Mongoose 7.8.3: Both instance and static validation work consistently
  • Mongoose 8.0.0: Inconsistent validation behavior emerges
  • Mongoose 8.8.3: Inconsistent validation behavior persists

Steps to Reproduce

const mongoose = require('mongoose');

// Interfaces (conceptual, for TypeScript-like clarity)
// interface Circle { kind: 'Circle'; radius: number; }
// interface PropertyPath { property: string; path: string; }
// interface Square { kind: 'Square'; propertyPaths: PropertyPath[]; }

async function runValidationTest() {
    // Create the base shape schema
    const shapeSchema = new mongoose.Schema({ name: String }, {
        discriminatorKey: 'kind',
        _id: false
    });

    // Main schema with shape array
    const schema = new mongoose.Schema({
        shape: [shapeSchema]
    });

    // Circle discriminator
    schema
        .path('shape')
        .discriminator('Circle', new mongoose.Schema({
            radius: {
                type: mongoose.Schema.Types.Number,
                required: true
            }
        }, { _id: false }));

    // PropertyPath schema for Square
    const propertyPathSchema = new mongoose.Schema({
        property: {
            type: mongoose.Schema.Types.String,
            required: true,
        },
        path: {
            type: mongoose.Schema.Types.String,
            required: true,
        }
    }, { _id: false });

    // Square discriminator
    schema
        .path('shape')
        .discriminator(
            'Square',
            new mongoose.Schema({
                propertyPaths: {
                    type: [propertyPathSchema],
                    required: true
                }
            }, { _id: false })
        );

    // Create the model
    const Model = mongoose.model('ShapeTest', schema);

    // Test cases
    const circle = { shape: [{ kind: 'Circle', radius: 5 }] };
    const square = { shape: [{ kind: 'Square', propertyPaths: [{}] }] };

    console.log("--- Instance Validation (new Model().validate()) ---");

    // Circle instance validation
    try {
        await new Model(circle).validate();
        console.log("Circle instance validation: PASSED ✓");
    } catch (error) {
        console.error("Circle instance validation: FAILED ✗", error);
    }

    // Square instance validation
    try {
        await new Model(square).validate();
        console.error("Square instance validation: UNEXPECTEDLY PASSED ✗");
    } catch (error) {
        console.log("Square instance validation: FAILED AS EXPECTED ✓");
        console.log("Validation Error:", error.message);
    }

    console.log("\n--- Static Validation (Model.validate()) ---");

    // Circle static validation
    try {
        await Model.validate(circle);
        console.log("Circle static validation: PASSED ✓");
    } catch (error) {
        console.error("Circle static validation: FAILED ✗", error);
    }

    // Square static validation
    try {
        await Model.validate(square);
        console.error("Square static validation: UNEXPECTEDLY PASSED ✗");
    } catch (error) {
        console.log("Square static validation: FAILED AS EXPECTED ✓");
        console.log("Validation Error:", error.message);
    }
}

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

Expected Behavior

Both new Model().validate() and Model.validate() should:

  1. Successfully validate circle documents with a radius
  2. Fail validation for square documents missing required propertyPaths properties

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmed-bugWe've confirmed this is a bug in Mongoose and will fix it.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions