{"record":{"id":"3ef89946ef3c6e65","repo":"jquense/yup","slug":"lazy-functions-must-return-a-valid-schema","errorCode":null,"errorMessage":"lazy() functions must return a valid schema","messagePattern":"lazy\\(\\) functions must return a valid schema","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/Lazy.ts","lineNumber":88,"sourceCode":"  clone(spec?: Partial<LazySpec>): Lazy<T, TContext, TFlags> {\n    const next = new Lazy<T, TContext, TFlags>(this.builder);\n    next.spec = { ...this.spec, ...spec };\n    return next;\n  }\n\n  private _resolve = (\n    value: any,\n    options: ResolveOptions<TContext> = {},\n  ): Schema<T, TContext, undefined, TFlags> => {\n    let schema = this.builder(value, options) as Schema<\n      T,\n      TContext,\n      undefined,\n      TFlags\n    >;\n\n    if (!isSchema(schema))\n      throw new TypeError('lazy() functions must return a valid schema');\n\n    if (this.spec.optional) schema = schema.optional();\n\n    return schema.resolve(options);\n  };\n\n  private optionality(optional: boolean) {\n    const next = this.clone({ optional });\n    return next;\n  }\n\n  optional(): Lazy<T | undefined, TContext, TFlags> {\n    return this.optionality(true);\n  }\n\n  resolve(options: ResolveOptions<TContext>) {\n    return this._resolve(options.value, options);\n  }","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/jquense/yup/blob/ff31eee8a2b10c938bb3a22544d9e33957a8cf01/src/Lazy.ts#L70-L106","documentation":"A lazy() schema defers to a getter function that must return a valid yup schema, which is then resolved on demand. Lazy.resolve checks the getter's return value with isSchema() and throws this TypeError if the function returned anything else (or nothing). It exists because lazy is commonly used for recursive structures where a bad return would otherwise fail deep inside validation.","triggerScenarios":"Calling yup.lazy(fn) where fn returns undefined (missing return), a plain object, a class instance that is not a schema, or a promise/other wrapper instead of a schema.","commonSituations":"Recursive schemas where the getter has a block body without `return this.schema()`; wrapping lazy around non-yup validators (e.g. a Zod schema); factory functions returning null when a config lookup fails.","solutions":["Make the lazy getter return a yup schema, e.g. () => yup.string() or () => this.schema().","If the getter is condition-based, ensure each branch returns a schema rather than relying on a default.","Check for typos in schema factory calls (yup.objec(), yup.strng()) producing undefined."],"exampleFix":"// before\nconst s = yup.lazy(() => { schemaDef.required(); })\n// after\nconst s = yup.lazy(() => schemaDef.required())","handlingStrategy":"type-guard","validationCode":"import { isSchema } from 'yup';\nconst safeLazy = (getter) => { const s = getter(); if (!isSchema(s)) throw new TypeError('lazy getter must return a schema'); return yup.lazy(getter); };","typeGuard":"const isValidLazyGetter = (fn) => isSchema(fn());","tryCatchPattern":"try {\n  return yup.lazy(getter);\n} catch (e) {\n  if (e.message.includes('lazy() functions must return')) {\n    // inspect what getter() returns\n  }\n  throw e;\n}","preventionTips":["Use single-expression arrows in lazy getters: () => yup.string().","Unit-test lazy schemas by calling resolve on them at startup.","Keep the getter pure and free of early `return;` statements."],"tags":["yup","schema","lazy","type-error"],"backgroundTag":"lazy-schema-invalid-return","analyzedSha":"ff31eee8a2b10c938bb3a22544d9e33957a8cf01","analyzedAt":"2026-08-31T21:51:03.773Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}