{"record":{"id":"3914132e703ff4a4","repo":"mastra-ai/mastra","slug":"union-must-have-at-least-2-options-391413","errorCode":null,"errorMessage":"Union must have at least 2 options","messagePattern":"Union must have at least 2 options","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/schema-compat/src/schema-compatibility-v4.ts","lineNumber":457,"sourceCode":"    const legacyDescription = value.description;\n\n    const description = this.mergeParameterDescription(metaDescription || legacyDescription, constraints);\n    if (description) {\n      result = result.describe(description);\n    }\n    return result;\n  }\n\n  /**\n   * Default handler for Zod union types. Processes all union options.\n   *\n   * @param value - The Zod union to process\n   * @returns The processed Zod union\n   * @throws Error if union has fewer than 2 options\n   */\n  public defaultZodUnionHandler(value: ZodUnion<[ZodAny, ...ZodAny[]]>): ZodAny {\n    const processedOptions = value._zod.def.options.map((option: ZodAny) => this.processZodType(option));\n    if (processedOptions.length < 2) throw new Error('Union must have at least 2 options');\n    let result = z.union(processedOptions as [ZodAny, ZodAny, ...ZodAny[]]);\n    if (value.description) {\n      result = result.describe(value.description);\n    }\n    // @ts-expect-error - fix later\n    return result;\n  }\n\n  /**\n   * Default handler for Zod string types. Processes string validation constraints.\n   *\n   * @param value - The Zod string to process\n   * @param handleChecks - String constraints to convert to descriptions vs keep as validation\n   * @returns The processed Zod string\n   */\n  public defaultZodStringHandler(\n    value: ZodString,\n    handleChecks: readonly StringCheckType[] = ALL_STRING_CHECKS,","sourceCodeStart":439,"sourceCodeEnd":475,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/schema-compat/src/schema-compatibility-v4.ts#L439-L475","documentation":"defaultZodUnionHandler in the Zod v4 compatibility path rebuilds unions from `value._zod.def.options` and requires at least 2 options because z.union() needs a tuple of 2+. This guard throws when a union with fewer than 2 options reaches the handler.","triggerScenarios":"A ZodUnion whose definition contains 0 or 1 options after processing — usually from dynamically built unions, or a single-option union created at runtime in Zod v4.","commonSituations":"Generated schemas (e.g. from OpenAPI/JSON Schema converters) that collapse to one branch; code that spreads optional/filtered option arrays into z.union; version migrations from Zod v3 unions.","solutions":["Provide at least two options in the union: z.union([a, b]).","When only one branch exists, return the branch type itself, unwrapped.","For runtime-built unions, branch on options.length before calling the compat layer.","Use z.optional()/z.nullable() instead of one-sided unions to express optionality."],"exampleFix":"// before\nconst maybe = z.union([z.string()]);\n// after\nconst maybe = z.union([z.string(), z.null()]); // or z.string().nullable()","handlingStrategy":"validation","validationCode":"function toUnionOrSingleV4(types: z.ZodTypeAny[]): z.ZodTypeAny {\n  if (types.length === 0) throw new TypeError('Need at least one type');\n  return types.length === 1 ? types[0] : z.union(types as [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]]);\n}","typeGuard":"function isValidUnionV4(u: { _zod: { def: { options: unknown[] } } }): boolean {\n  return u._zod.def.options.length >= 2;\n}","tryCatchPattern":"try {\n  processed = compat.process(schema);\n} catch (e) {\n  if (e.message === 'Union must have at least 2 options') {\n    throw new Error('Dynamic union collapsed; ensure >=2 branches or unwrap', { cause: e });\n  }\n  throw e;\n}","preventionTips":["Guard dynamic option arrays before z.union.","Replace one-branch unions with the branch itself or z.nullable/z.optional.","Test generated schemas after Zod v3→v4 migration."],"tags":["zod","zod-v4","union"],"backgroundTag":"union-too-few-options","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}