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
- Provide a non-empty array: `of: [String, 'ObjectId', Number]`
- 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
- 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
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
- A setter must be a function.
- A getter must be a function.
- Arguments must be aggregate pipeline operators
- Aggregate `near()` must be called with non-nullish argument
- Aggregate `near()` argument must have a `near` property
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/1571384a0c7e12ea.
Report an issue: GitHub.