{"record":{"id":"1571384a0c7e12ea","repo":"Automattic/mongoose","slug":"union-schema-type-requires-an-array-of-types","errorCode":null,"errorMessage":"Union schema type requires an array of types","messagePattern":"Union schema type requires an array of types","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/schema/union.js","lineNumber":28,"sourceCode":"const firstValueSymbol = Symbol('firstValue');\n\n/*!\n * ignore\n */\n\nclass Union extends SchemaType {\n  /**\n   * Create a Union schema type.\n   *\n   * @param {string} key the path in the schema for this schema type\n   * @param {object} options SchemaType-specific options (must have 'of' as array)\n   * @param {object} schemaOptions additional options from the schema this schematype belongs to\n   * @param {Schema} parentSchema the schema this schematype belongs to\n   */\n  constructor(key, options, schemaOptions, parentSchema) {\n    super(key, options, 'Union', parentSchema);\n    if (!Array.isArray(options?.of) || options.of.length === 0) {\n      throw new Error('Union schema type requires an array of types');\n    }\n    this.schemaTypes = options.of.map(obj => parentSchema.interpretAsType(key, obj, schemaOptions));\n    this.$isSchemaUnion = true;\n  }\n\n  cast(val, doc, init, prev, options) {\n    let firstValue = firstValueSymbol;\n    let lastError;\n    // Loop through each schema type in the union. If one of the schematypes returns a value that is `=== val`, then\n    // use `val`. Otherwise, if one of the schematypes casted successfully, use the first successfully casted value.\n    // Finally, if none of the schematypes casted successfully, throw the error from the last schema type in the union.\n    // The `=== val` check is a workaround to ensure that the original value is returned if it matches one of the schema types,\n    // avoiding cases like where numbers are casted to strings or dates even if the schema type is a number.\n    for (let i = 0; i < this.schemaTypes.length; ++i) {\n      try {\n        const casted = this.schemaTypes[i].cast(val, doc, init, prev, options);\n        if (casted === val) {\n          return casted;","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/schema/union.js#L10-L46","documentation":"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.","triggerScenarios":"`new Schema({ data: { type: 'Union' } })` (no `of`); `{ type: 'Union', of: [] }`; `of: 'string'` (string shorthand is not accepted — it must be an array).","commonSituations":"Typos in the `of` key; assuming a single-type string shorthand works; migrating a Mixed field to Union and forgetting the type list.","solutions":["Provide a non-empty array: `of: [String, 'ObjectId', Number]`","Ensure each entry is a type `interpretAsType` understands (native constructors, type-name strings, schema instances)"],"exampleFix":"// before\nnew Schema({ data: { type: 'Union' } }); // throws\n\n// after\nnew Schema({ data: { type: 'Union', of: [String, Schema.Types.ObjectId] } });","handlingStrategy":"validation","validationCode":"function assertUnionOptions(options) {\n  if (!Array.isArray(options?.of) || options.of.length === 0) {\n    throw new TypeError('Union type requires a non-empty \"of\" array, e.g. of: [String, Schema.Types.ObjectId]');\n  }\n}","typeGuard":"const hasValidUnionTypes = options => Array.isArray(options?.of) && options.of.length > 0;","tryCatchPattern":null,"preventionTips":["Define union field definitions once, in a shared constant, so `of` is never omitted","Remember `of` must be an array even for a single candidate type"],"tags":["mongoose","schema","union","types","validation"],"backgroundTag":"invalid-schema-option","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}