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

  1. Validate/derive the bound before passing it: ensure it is a finite number (`Number.isFinite`).
  2. If the limit is intentionally unbounded, omit the option entirely instead of passing Infinity.
  3. 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

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


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