can1357/oh-my-pi · error · OmpTypeError
${name} must be finite
Error message
${name} must be finite What it means
tString/tNumber/tArray option builders validate that numeric constraint options (minLength, maxLength, minimum, maximum, exclusive bounds, multipleOf, length, etc.) are finite numbers via `checkFiniteOption`. Passing NaN, Infinity, or -Infinity for any such option throws `<name> must be finite` at construction time rather than producing a broken schema.
Source
Thrown at packages/omptype/src/typebox.ts:200
return compatSchema;
}
function applyMeta<T>(schema: RuntimeType<T>, opts?: Meta): CompatRuntime<T> {
let result = schema;
const description = opts?.description ?? opts?.title;
if (description !== undefined) result = result.describe(description);
if (opts && Object.hasOwn(opts, "default")) result = result.default(opts.default as T);
return withLegacyCompat(result);
}
function withJsonSchemaKeywords<T>(schema: CompatRuntime<T>, keywords: Record<string, unknown>): CompatRuntime<T> {
const emitBase = schema.toJsonSchema.bind(schema);
schema.toJsonSchema = options => ({ ...emitBase(options), ...keywords });
return schema;
}
function checkFiniteOption(name: string, value: number | undefined): void {
if (value !== undefined && !Number.isFinite(value)) throw new OmpTypeError(`${name} must be finite`);
}
function tString(opts?: StringOpts): TString {
checkFiniteOption("minLength", opts?.minLength);
checkFiniteOption("maxLength", opts?.maxLength);
let schema = asRuntime<string>(type.raw(opts?.format === "url" || opts?.format === "uri" ? "string.url" : "string"));
if (opts?.minLength !== undefined) schema = schema.atLeastLength(opts.minLength);
if (opts?.maxLength !== undefined) schema = schema.atMostLength(opts.maxLength);
if (opts?.pattern !== undefined) {
let regex: RegExp;
try {
regex = new RegExp(opts.pattern);
} catch {
throw new OmpTypeError(`invalid regular expression pattern ${JSON.stringify(opts.pattern)}`);
}
schema = schema.narrow((value, ctx) => regex.test(value) || ctx.mustBe(`a string matching ${opts.pattern}`));
}
if (opts?.format !== undefined && opts.format !== "url" && opts.format !== "uri") {View on GitHub (pinned to 9690622007)
Solutions
- Validate/derive the bound before passing it: ensure it is a finite number (`Number.isFinite`).
- If the limit is intentionally unbounded, omit the option entirely instead of passing Infinity.
- Fix the computation producing NaN (empty array input, undefined config value, failed parseInt).
Example fix
// before
tString({ maxLength: Number(process.env.MAX_LEN) }); // NaN if unset
// after
const raw = Number(process.env.MAX_LEN);
tString(raw !== undefined && Number.isFinite(raw) ? { maxLength: raw } : undefined); Defensive patterns
Strategy: validation
Validate before calling
function finiteOpt(n: number | undefined): number | undefined {
if (n === undefined) return undefined;
if (!Number.isFinite(n)) throw new RangeError(`bound must be finite, got ${n}`);
return n;
}
// use: tString({ maxLength: finiteOpt(rawMaxLength) }) Type guard
function isFiniteBound(v: unknown): v is number {
return typeof v === "number" && Number.isFinite(v);
} Try / catch
try {
const schema = tString({ maxLength: opts.maxLength });
} catch (err) {
if (err instanceof Error && /must be finite$/.test(err.message)) {
throw new Error(`Invalid schema option from config: ${err.message}`);
}
throw err;
} Prevention
- Always run config-derived numeric options through Number.isFinite before use.
- Treat Infinity as 'no constraint' — omit the option instead of passing it.
- Parse limits with a helper that returns `number | undefined` (undefined on unparsable input) instead of raw Number().
When it happens
Trigger: `tString({ maxLength: Infinity })`, `tString({ minLength: NaN })`, `tArray(el, { length: Infinity })`, or a computed bound like `tNumber({ maximum: someMax })` where `someMax` is NaN/Infinity (e.g. result of `Math.max()` on empty input, division by zero, parsed untrusted config).
Common situations: Deriving limits from unvalidated JSON config or env vars (`parseInt` returning NaN); computing `Infinity` as a default max; arithmetic overflows when converting byte limits to string limits.
Related errors
- multipleOf must be greater than zero
- invalid regular expression pattern ${JSON.stringify(opts.pat
- Type.${operation} requires a schema created by Type.Object
- ${kind} length must be a nonnegative safe integer
- object mode requires an object schema
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f15f8874fad9fc7b.
Report an issue: GitHub.