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
- Pass a callable function to implement().
- If the handler is optional, guard with a conditional before calling implement().
- Verify the imported symbol is actually a function (check for circular imports returning undefined).
- 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
- Verify the handler reference is defined and callable before passing to implement().
- Watch for circular imports that yield undefined.
- Type the handler parameter explicitly so tsc flags non-functions.
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
- implementAsync() must be called with a function
- Invalid element at key
- A discriminator value for key
- Can't use "invalid_type_error" or "required_error" in…
- Cannot create literal schema with no valid values
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)