can1357/oh-my-pi · error · OmpTypeError

object mode requires an object schema

Error message

object mode requires an object schema

What it means

The zod-compat methods `.strict()`, `.passthrough()`, and `.strip()` only make sense for object schemas; they call `withObjectExtras`, which throws `object mode requires an object schema` when the underlying schema's IR kind is not `object`.

Source

Thrown at packages/omptype/src/zod.ts:109

function isStringKeyIR(ir: IR): boolean {
	switch (ir.k) {
		case "string":
			return true;
		case "lit":
			return typeof ir.v === "string";
		case "union":
			return ir.members.length > 0 && ir.members.every(isStringKeyIR);
		case "sub":
			return isStringKeyIR(ir.schema.ir);
		default:
			return false;
	}
}

function decorate<Out>(schema: Decoratable<Out>, optional = false): ZodLikeSchema<Out> {
	const next = (inner: Decoratable<Out>, nextOptional = optional): ZodLikeSchema<Out> => decorate(inner, nextOptional);
	const withObjectExtras = (extras: "keep" | "reject" | "delete"): ZodLikeSchema<Out> => {
		if (schema.ir.k !== "object") throw new OmpTypeError("object mode requires an object schema");
		return next(restrictBase(schema, { ...schema.ir, extras }));
	};
	Object.defineProperty(schema, "isOptional", { value: optional, enumerable: false });

	return Object.assign(schema, {
		parse(value: unknown): Out {
			const result = schema(value);
			if (result instanceof type.errors) throw new Error(result.summary);
			return result;
		},
		safeParse(value: unknown): ZodLikeSafeParseResult<Out> {
			const result = schema(value);
			if (!(result instanceof type.errors)) return { success: true, data: result };
			return {
				success: false,
				error: {
					message: result.summary,
					issues: result.map(issue => ({ path: [...issue.path], message: issue.problem })),

View on GitHub (pinned to 9690622007)

Solutions

  1. Only call `.strict()`/`.passthrough()`/`.strip()` on `z.object({...})` schemas.
  2. If the schema kind varies, gate the call: `if (schema.ir.k === 'object') schema.strict()` or use a typed TObject parameter.
  3. Remove the mode call if the intent was only decoration — these methods are object-specific.

Example fix

// before
function define(s: any) { return s.strict(); }
define(z.string()); // throws
// after
function define(s: ZodLikeSchema<{ id: string }>) { return s.strict(); }
define(z.object({ id: z.string() }));
Defensive patterns

Strategy: type-guard

Validate before calling

// narrow to object schemas before applying modes
if (schema.ir.k !== "object") throw new Error("strict()/passthrough() require an object schema");
schema.strict();

Type guard

function isObjectSchema(s: ZodLikeSchema<unknown>): boolean {
  return (s as unknown as { ir: { k: string } }).ir.k === "object";
}

Try / catch

try {
  const strict = maybeSchema.strict();
} catch (err) {
  if (err instanceof Error && err.message === 'object mode requires an object schema') {
    return maybeSchema; // modes only apply to objects — pass through unchanged
  }
  throw err;
}

Prevention

When it happens

Trigger: `z.string().strict()`, `z.array(el).passthrough()`, `z.object({...}).strict()` is fine but `z.union([...]).strict()` throws; also calling strict/passthrough/strip on a schema that was narrowed from object to something else.

Common situations: Generic wrapper functions that unconditionally chain `.strict()` on schemas of varying kinds; copying a builder chain to a non-object type during refactors.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/063b2c54ad0de70e. Report an issue: GitHub.