Automattic/mongoose · error · Error

Union schema type requires an array of types

Error message

Union schema type requires an array of types

What it means

The Union schema type (`Schema.Types.Union`, also `type: 'Union'`) requires an `of` option that is a non-empty array; each entry is interpreted as a type by the parent schema (lib/schema/union.js:28). A missing `of`, an empty array, or a non-array `of` throws at schema build time.

Source

Thrown at lib/schema/union.js:28

const firstValueSymbol = Symbol('firstValue');

/*!
 * ignore
 */

class Union extends SchemaType {
  /**
   * Create a Union schema type.
   *
   * @param {string} key the path in the schema for this schema type
   * @param {object} options SchemaType-specific options (must have 'of' as array)
   * @param {object} schemaOptions additional options from the schema this schematype belongs to
   * @param {Schema} parentSchema the schema this schematype belongs to
   */
  constructor(key, options, schemaOptions, parentSchema) {
    super(key, options, 'Union', parentSchema);
    if (!Array.isArray(options?.of) || options.of.length === 0) {
      throw new Error('Union schema type requires an array of types');
    }
    this.schemaTypes = options.of.map(obj => parentSchema.interpretAsType(key, obj, schemaOptions));
    this.$isSchemaUnion = true;
  }

  cast(val, doc, init, prev, options) {
    let firstValue = firstValueSymbol;
    let lastError;
    // Loop through each schema type in the union. If one of the schematypes returns a value that is `=== val`, then
    // use `val`. Otherwise, if one of the schematypes casted successfully, use the first successfully casted value.
    // Finally, if none of the schematypes casted successfully, throw the error from the last schema type in the union.
    // The `=== val` check is a workaround to ensure that the original value is returned if it matches one of the schema types,
    // avoiding cases like where numbers are casted to strings or dates even if the schema type is a number.
    for (let i = 0; i < this.schemaTypes.length; ++i) {
      try {
        const casted = this.schemaTypes[i].cast(val, doc, init, prev, options);
        if (casted === val) {
          return casted;

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Provide a non-empty array: `of: [String, 'ObjectId', Number]`
  2. Ensure each entry is a type `interpretAsType` understands (native constructors, type-name strings, schema instances)

Example fix

// before
new Schema({ data: { type: 'Union' } }); // throws

// after
new Schema({ data: { type: 'Union', of: [String, Schema.Types.ObjectId] } });
Defensive patterns

Strategy: validation

Validate before calling

function assertUnionOptions(options) {
  if (!Array.isArray(options?.of) || options.of.length === 0) {
    throw new TypeError('Union type requires a non-empty "of" array, e.g. of: [String, Schema.Types.ObjectId]');
  }
}

Type guard

const hasValidUnionTypes = options => Array.isArray(options?.of) && options.of.length > 0;

Prevention

When it happens

Trigger: `new Schema({ data: { type: 'Union' } })` (no `of`); `{ type: 'Union', of: [] }`; `of: 'string'` (string shorthand is not accepted — it must be an array).

Common situations: Typos in the `of` key; assuming a single-type string shorthand works; migrating a Mixed field to Union and forgetting the type list.

Related errors


AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21). Data as JSON: /api/errors/1571384a0c7e12ea. Report an issue: GitHub.