can1357/oh-my-pi · error · OmpTypeError

numeric bound must be finite

Error message

numeric bound must be finite

What it means

withNumericBound rejects non-finite bounds (NaN, Infinity, -Infinity) before applying them to a numeric IR. Bounds must be finite so the resulting range is meaningful; passing Infinity as an 'unbounded' marker is not allowed.

Source

Thrown at packages/omptype/src/type.ts:2467

	if (b === undefined) return a;
	return Math.max(a, b);
}

function minOf(a: number | undefined, b: number | undefined): number | undefined {
	if (a === undefined) return b;
	if (b === undefined) return a;
	return Math.min(a, b);
}

function withLengthBound(ir: IR, side: "min" | "max", bound: number): IR {
	if (ir.k === "array" || ir.k === "string") {
		return side === "min" ? { ...ir, min: bound } : { ...ir, max: bound };
	}
	throw new OmpTypeError(`cannot apply length bound to ${ir.k}`);
}

function withNumericBound(ir: IR, side: "min" | "max", bound: number, exclusive = false): IR {
	if (!Number.isFinite(bound)) throw new OmpTypeError("numeric bound must be finite");
	if (ir.k === "number") {
		return side === "min" ? { ...ir, min: bound, xmin: exclusive } : { ...ir, max: bound, xmax: exclusive };
	}
	if (ir.k === "union") {
		return { ...ir, members: ir.members.map(member => withNumericBound(member, side, bound, exclusive)) };
	}
	throw new OmpTypeError(`cannot apply numeric bound to ${ir.k}`);
}
interface GenericParameter {
	readonly name: string;
	readonly constraintDef?: unknown;
}

interface GenericMeta {
	readonly parameters: readonly GenericParameter[];
	instantiateIR(arguments_: readonly IR[]): IR;
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Replace non-finite bounds with undefined (omit the bound) instead of Infinity sentinels
  2. Sanitize computed values: check Number.isFinite(bound) before passing it
  3. Fix the source computation producing NaN (guard division/parse failures)
  4. Catch OmpTypeError and fall back to an unconstrained number

Example fix

// before
number().max(Math.max(...items.map(i => i.cap))); // -Infinity when items is empty
// after
const bound = Math.max(...items.map(i => i.cap));
const t = Number.isFinite(bound) ? number().max(bound) : number();
Defensive patterns

Strategy: validation

Validate before calling

function finiteBound(n) {
  return typeof n === 'number' && Number.isFinite(n) ? n : undefined;
}
// usage: number().max(finiteBound(computedMax)) — undefined omits the bound

Type guard

const isFiniteBound = (n: unknown): n is number => typeof n === 'number' && Number.isFinite(n);

Try / catch

try {
  const t = number().min(b);
} catch (err) {
  if (err instanceof OmpTypeError && err.message === 'numeric bound must be finite') {
    // fall back to unconstrained number() and warn
  } else throw err;
}

Prevention

When it happens

Trigger: number().min(Infinity), number().max(-Infinity), number().min(Number.NaN); or computed bounds like Math.max() over an empty list (-Infinity) or a division by zero producing NaN fed into a builder.

Common situations: Deriving bounds from config data where a field is missing/NaN; using Number.MAX_VALUE vs Infinity confusion aside, sentinel values like Infinity meaning 'no limit'; empty-array Math.min/max results.

Related errors


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