colinhacks/zod · error · Error

implement() must be called with a function

Error message

implement() must be called with a function

What it means

Thrown by inst.implement() on a $ZodFunction when the argument is not of type "function". implement() wraps a user function so that its arguments and return value are parsed against the function schema's input/output schemas; passing anything other than a callable function (a number, object, undefined, null, string) is rejected immediately at the wrap site. The check is `typeof func !== "function"`.

Solutions

  1. Pass a callable function to implement().
  2. If the handler is optional, guard with a conditional before calling implement().
  3. Verify the imported symbol is actually a function (check for circular imports returning undefined).
  4. Add a unit test asserting typeof handler === "function" before wiring.

Example fix

// before
const fn = z.function(z.string(), z.string()).implement(undefined);
// after
const fn = z.function(z.string(), z.string()).implement((s) => s.toUpperCase());
Defensive patterns

Strategy: type-guard

Validate before calling

function implementOrThrow<F>(fnSchema: z.ZodFunction, fn: F) {
  if (typeof fn !== "function") throw new Error("implement() requires a function");
  return fnSchema.implement(fn as any);
}

Type guard

function isFunction(v: unknown): v is Function {
  return typeof v === "function";
}

Prevention

When it happens

Trigger: Calling z.function(...).implement(null), .implement({}), .implement(42), .implement(undefined), or .implement("foo"). Also triggered by passing a value that was supposed to be a function but was overwritten or never assigned (undefined from a failed import).

Common situations: Forgetting to pass the handler; passing a class instance or bound object instead of the function; conditional logic that yields a non-function in a branch; refactoring that renames the function but leaves the old reference (now undefined); misuse of the API by treating implement() like a setter.

Related errors


AI-assisted analysis of colinhacks/zod@2d90846af9 (2026-08-11). Data as JSON: /api/errors/fd19a44cf3783f59. Report an issue: GitHub.

Appendix: source

Thrown at packages/zod/src/v4/core/schemas.ts:4449

  output<NewReturns extends $ZodType>(output: NewReturns): $ZodFunction<Args, NewReturns>;
}

export interface $ZodFunctionParams<I extends $ZodFunctionIn, O extends $ZodType> {
  input?: I;
  output?: O;
}

export const $ZodFunction: core.$constructor<$ZodFunction> = /*@__PURE__*/ core.$constructor(
  "$ZodFunction",
  (inst, def) => {
    $ZodType.init(inst, def);
    inst._def = def;
    inst._zod.def = def;

    inst.implement = (func) => {
      if (typeof func !== "function") {
        throw new Error("implement() must be called with a function");
      }
      return function (this: any, ...args: never[]) {
        const parsedArgs = inst._def.input ? parse(inst._def.input, args) : args;
        const result = Reflect.apply(func, this, parsedArgs as never[]);
        if (inst._def.output) {
          return parse(inst._def.output, result);
        }
        return result as any;
      };
    };

    inst.implementAsync = (func) => {
      if (typeof func !== "function") {
        throw new Error("implementAsync() must be called with a function");
      }
      return async function (this: any, ...args: never[]) {
        const parsedArgs = inst._def.input ? await parseAsync(inst._def.input, args) : args;
        const result = await Reflect.apply(func, this, parsedArgs as never[]);

View on GitHub (pinned to 2d90846af9)