can1357/oh-my-pi · error · OmpTypeError

number max must not be NaN

Error message

number max must not be NaN

What it means

omptype's Zod-like `.max()` on a number schema rejects NaN bounds. A NaN bound would make every comparison meaningless and silently invalidate all values, so the library throws an OmpTypeError at schema-construction time instead of failing at validation time. This is a fail-fast guard in `max()` at packages/omptype/src/zod.ts:153.

Source

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

				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}`);
		},
		int(): ZodLikeSchema<Out> {
			if (schema.ir.k !== "number") throw new OmpTypeError(`cannot apply int to ${schema.ir.k}`);
			return next(restrictBase(schema, { ...schema.ir, int: true }));
		},
		positive(): ZodLikeSchema<Out> {
			if (schema.ir.k !== "number") throw new OmpTypeError(`cannot apply positive to ${schema.ir.k}`);
			const ir = schema.ir;
			if (ir.min !== undefined && ir.min > 0) return next(restrictBase(schema, ir));
			return next(restrictBase(schema, { ...ir, min: 0, xmin: true }));
		},
		nonnegative(): ZodLikeSchema<Out> {
			if (schema.ir.k !== "number") throw new OmpTypeError(`cannot apply nonnegative to ${schema.ir.k}`);
			return this.min(0);

View on GitHub (pinned to 9690622007)

Solutions

  1. Check the bound with Number.isNaN() before calling .max() and use a sane fallback
  2. Trace where the bound value is computed; fix the parse/lookup that produced NaN
  3. If 'no limit' is intended, skip calling .max() entirely instead of passing NaN

Example fix

// before
const limit = Number(process.env.MAX_ITEMS);
schema.max(limit); // throws when MAX_ITEMS is undefined
// after
const parsed = Number(process.env.MAX_ITEMS);
const limit = Number.isNaN(parsed) ? 100 : parsed;
schema.max(limit);
Defensive patterns

Strategy: validation

Validate before calling

function assertFiniteBound(bound: number, name = "max"): void {
  if (!Number.isFinite(bound)) throw new Error(`${name} bound must be finite, got ${bound}`);
}
assertFiniteBound(limit); // before schema.max(limit)

Type guard

function isFiniteNumber(v: unknown): v is number {
  return typeof v === "number" && Number.isFinite(v);
}

Try / catch

try {
  schema = base.max(bound);
} catch (err) {
  if (err instanceof OmpTypeError && /NaN/.test(err.message)) {
    schema = base; // skip the bound and log the bad input
  } else throw err;
}

Prevention

When it happens

Trigger: Calling `.max(bound)` on a number schema where `Number.isNaN(bound)` is true — typically `.max(someNumber)` where someNumber came from a failed parse (parseFloat/Number of undefined, empty string, or missing config).

Common situations: Config values read from env or JSON that are undefined or non-numeric, then passed to Number()/parseFloat() yielding NaN; arithmetic on missing fields; data-driven bounds from a file with a blank cell.

Related errors


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