can1357/oh-my-pi · error · OmpTypeError

multipleOf must be greater than zero

Error message

multipleOf must be greater than zero

What it means

Beyond finiteness, `tNumber` requires `multipleOf` to be strictly greater than zero. A zero or negative multipleOf is mathematically meaningless for the constraint (every value is a multiple of... nothing) and is rejected at schema construction with this message.

Source

Thrown at packages/omptype/src/typebox.ts:262

		case "uuid":
			return value => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
		case "date-time":
			return value =>
				/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d+)?(?:Z|[+-]\d\d:\d\d)$/.test(value) &&
				!Number.isNaN(Date.parse(value));
		case "date":
			return value => /^\d{4}-\d\d-\d\d$/.test(value) && !Number.isNaN(Date.parse(`${value}T00:00:00Z`));
		default:
			return () => true;
	}
}

function tNumber(opts?: NumberOpts, integer = false): TNumber {
	for (const key of ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"] as const) {
		checkFiniteOption(key, opts?.[key]);
	}
	if (opts?.multipleOf !== undefined && opts.multipleOf <= 0)
		throw new OmpTypeError("multipleOf must be greater than zero");
	let lower: { value: number; exclusive: boolean } | undefined;
	if (opts?.minimum !== undefined) lower = { value: opts.minimum, exclusive: false };
	if (opts?.exclusiveMinimum !== undefined && (!lower || opts.exclusiveMinimum >= lower.value)) {
		lower = { value: opts.exclusiveMinimum, exclusive: true };
	}
	let upper: { value: number; exclusive: boolean } | undefined;
	if (opts?.maximum !== undefined) upper = { value: opts.maximum, exclusive: false };
	if (opts?.exclusiveMaximum !== undefined && (!upper || opts.exclusiveMaximum <= upper.value)) {
		upper = { value: opts.exclusiveMaximum, exclusive: true };
	}
	const keyword = integer ? "number.integer" : "number";
	// The `LO <= TYPE <= HI` range spelling requires both bounds; a min-only
	// bound must use the postfix `TYPE >= LO` form (see parseBounded in ir.ts).
	let src: string;
	if (lower && upper) {
		src = `${lower.value} ${lower.exclusive ? "<" : "<="} ${keyword} ${upper.exclusive ? "<" : "<="} ${upper.value}`;
	} else if (lower) {
		src = `${keyword} ${lower.exclusive ? ">" : ">="} ${lower.value}`;

View on GitHub (pinned to 9690622007)

Solutions

  1. Ensure the value passed is > 0; validate with `step > 0` before constructing the schema.
  2. If no step constraint is desired, omit `multipleOf` instead of passing 0.
  3. Use `Math.abs()` only if a negative step is acceptable as a magnitude — then pass the absolute value.

Example fix

// before
tNumber({ multipleOf: options.step }); // step may be 0
// after
const step = options.step > 0 ? options.step : undefined;
tNumber(step !== undefined ? { multipleOf: step } : undefined);
Defensive patterns

Strategy: validation

Validate before calling

function positiveMultipleOf(n: number | undefined): number | undefined {
  if (n === undefined) return undefined;
  if (!(typeof n === 'number' && Number.isFinite(n) && n > 0)) throw new RangeError(`multipleOf must be > 0, got ${n}`);
  return n;
}

Type guard

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

Try / catch

try {
  const schema = tNumber({ multipleOf: step });
} catch (err) {
  if (err instanceof Error && err.message === 'multipleOf must be greater than zero') {
    throw new Error(`Configured step must be a positive number, got ${step}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: `tNumber({ multipleOf: 0 })`, `tNumber({ multipleOf: -2 })`, or `multipleOf` computed from a variable that ends up 0/negative (e.g. step derived from config defaulting to 0).

Common situations: Building dynamic numeric schemas where the step comes from user config or a UI input that can be 0; sign errors when deriving step from a direction-aware value.

Related errors


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