can1357/oh-my-pi · error · OmpTypeError
number min must not be NaN
Error message
number min must not be NaN
What it means
In the zod-compat layer, `.min()` applied to a number schema rejects NaN bounds with `number min must not be NaN` (the length-bound safe-integer rule does not apply to numbers, but NaN is still meaningless as a minimum). It is thrown at builder time, before any value is validated.
Source
Thrown at packages/omptype/src/zod.ts:139
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 })),
},
};
},
min(bound: number): ZodLikeSchema<Out> {
const ir = schema.ir;
if (ir.k === "string" || ir.k === "array") {
lengthBound("min", schema, bound);
const min = ir.min === undefined ? bound : Math.max(ir.min, bound);
return next(restrictBase(schema, { ...ir, min }));
}
if (ir.k === "number") {
if (Number.isNaN(bound)) throw new OmpTypeError("number min must not be NaN");
if (ir.min !== undefined && ir.min >= bound) return next(restrictBase(schema, ir));
return next(restrictBase(schema, { ...ir, min: bound, xmin: false }));
}
throw new OmpTypeError(`cannot apply min to ${ir.k}`);
},
max(bound: number): ZodLikeSchema<Out> {
const ir = schema.ir;
if (ir.k === "string" || ir.k === "array") {
lengthBound("max", schema, bound);
const max = ir.max === undefined ? bound : Math.min(ir.max, bound);
return next(restrictBase(schema, { ...ir, max }));
}
if (ir.k === "number") {
if (Number.isNaN(bound)) throw new OmpTypeError("number max must not be NaN");
if (ir.max !== undefined && ir.max <= bound) return next(restrictBase(schema, ir));
return next(restrictBase(schema, { ...ir, max: bound, xmax: false }));
}
throw new OmpTypeError(`cannot apply max to ${ir.k}`);View on GitHub (pinned to 9690622007)
Solutions
- Validate the bound before calling: `if (!Number.isNaN(min)) schema.min(min)`.
- Fix the upstream computation that yields NaN (default the config value, guard division).
- Omit the `.min()` call when no numeric lower bound is intended.
Example fix
// before num.min(Number(opts.minimum)); // NaN when opts.minimum undefined // after const m = Number(opts.minimum); if (Number.isFinite(m)) num.min(m);
Defensive patterns
Strategy: validation
Validate before calling
function finiteMin(n: number | undefined): number | undefined {
if (n === undefined) return undefined;
if (Number.isNaN(n)) throw new RangeError('number min must not be NaN');
return n;
}
// use: num.min(finiteMin(rawMin)!) Type guard
function isNumericMin(v: unknown): v is number {
return typeof v === "number" && !Number.isNaN(v);
} Try / catch
try {
const s = z.number().min(rawMin);
} catch (err) {
if (err instanceof Error && err.message === 'number min must not be NaN') {
throw new Error(`Minimum limit config value is not a number`);
}
throw err;
} Prevention
- Guard Number()/parseFloat results with Number.isNaN before using as bounds.
- Default unset config values instead of letting undefined become NaN through Number().
- Omit .min() when the bound cannot be determined.
When it happens
Trigger: `z.number().min(Number.NaN)` or `z.number().min(someVar)` where `someVar` is NaN — commonly `Number(undefined)`, a failed `parseFloat` of config, or `0/0` in a computed default.
Common situations: Deriving minimum limits from env/config that is unset or unparsable; spreading optional option objects where the min field was computed earlier and became NaN.
Related errors
- ${kind} length must be a nonnegative safe integer
- ${name} must be finite
- invalid regular expression pattern ${JSON.stringify(opts.pat
- multipleOf must be greater than zero
- object mode requires an object schema
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/c4bd04a73b772b17.
Report an issue: GitHub.