jquense/yup · error · TypeError

lazy() functions must return a valid schema

Error message

lazy() functions must return a valid schema

What it means

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.

Source

Thrown at src/Lazy.ts:88

  clone(spec?: Partial<LazySpec>): Lazy<T, TContext, TFlags> {
    const next = new Lazy<T, TContext, TFlags>(this.builder);
    next.spec = { ...this.spec, ...spec };
    return next;
  }

  private _resolve = (
    value: any,
    options: ResolveOptions<TContext> = {},
  ): Schema<T, TContext, undefined, TFlags> => {
    let schema = this.builder(value, options) as Schema<
      T,
      TContext,
      undefined,
      TFlags
    >;

    if (!isSchema(schema))
      throw new TypeError('lazy() functions must return a valid schema');

    if (this.spec.optional) schema = schema.optional();

    return schema.resolve(options);
  };

  private optionality(optional: boolean) {
    const next = this.clone({ optional });
    return next;
  }

  optional(): Lazy<T | undefined, TContext, TFlags> {
    return this.optionality(true);
  }

  resolve(options: ResolveOptions<TContext>) {
    return this._resolve(options.value, options);
  }

View on GitHub (pinned to ff31eee8a2)

Solutions

  1. Make the lazy getter return a yup schema, e.g. () => yup.string() or () => this.schema().
  2. If the getter is condition-based, ensure each branch returns a schema rather than relying on a default.
  3. Check for typos in schema factory calls (yup.objec(), yup.strng()) producing undefined.

Example fix

// before
const s = yup.lazy(() => { schemaDef.required(); })
// after
const s = yup.lazy(() => schemaDef.required())
Defensive patterns

Strategy: type-guard

Validate before calling

import { isSchema } from 'yup';
const safeLazy = (getter) => { const s = getter(); if (!isSchema(s)) throw new TypeError('lazy getter must return a schema'); return yup.lazy(getter); };

Type guard

const isValidLazyGetter = (fn) => isSchema(fn());

Try / catch

try {
  return yup.lazy(getter);
} catch (e) {
  if (e.message.includes('lazy() functions must return')) {
    // inspect what getter() returns
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jquense/yup@ff31eee8a2 (2026-08-31). Data as JSON: /api/errors/3ef89946ef3c6e65. Report an issue: GitHub.