can1357/oh-my-pi · error · OmpTypeError
expected bound after comparator in "${this.#src}"
Error message
expected bound after comparator in "${this.#src}" What it means
A comparison operator ('<', '<=', '>', '>=', '==') was parsed, but the following token is not a numeric or date literal — the bound value is missing or of the wrong kind. Bounds must be num or date tokens; identifiers, keywords, strings, or end-of-input after a comparator trigger this.
Source
Thrown at packages/omptype/src/ir.ts:444
parseBounded(): IR {
const t = this.#peek();
const t1 = this.#peek(1);
if ((t?.t === "num" || t?.t === "date") && t1?.t === "op" && (t1.v === "<" || t1.v === "<=")) {
const lo = t.v;
this.#pos += 2;
let node = this.#eatDivisor(this.parsePostfix());
node = applyBound(node, flip(t1.v), lo, this.#src);
const t2 = this.#peek();
if (!(t2?.t === "op" && CMP[t2.v])) {
throw new OmpTypeError(`left bound requires a corresponding right bound in "${this.#src}"`);
}
if (t2.v === ">" || t2.v === ">=") {
throw new OmpTypeError(`right bound must use < or <= in "${this.#src}"`);
}
this.#pos++;
const hi = this.#next();
if (hi.t !== "num" && hi.t !== "date") {
throw new OmpTypeError(`expected bound after comparator in "${this.#src}"`);
}
node = applyBound(node, t2.v, hi.v, this.#src);
return this.#eatArraySuffixes(node);
}
let node = this.#eatDivisor(this.parsePostfix());
const t2 = this.#peek();
if (t2?.t === "op" && t2.v === "==") {
this.#pos++;
const limit = this.#next();
if (limit.t !== "num" && limit.t !== "bigint" && limit.t !== "date") {
throw new OmpTypeError(`expected literal after == in "${this.#src}"`);
}
node = applyEquality(node, limit.v, this.#src);
} else if (t2?.t === "op" && CMP[t2.v] && (this.#peek(1)?.t === "num" || this.#peek(1)?.t === "date")) {
this.#pos++;
const limit = this.#next() as Extract<Tok, { t: "num" | "date" }>;
node = applyBound(node, t2.v, limit.v, this.#src);
node = this.#eatArraySuffixes(node);View on GitHub (pinned to 9690622007)
Solutions
- Replace the bound with a numeric or date literal: 'number > 5', 'Date < d"2024-01-01"'.
- If the bound comes from a variable, stringify the raw number before interpolation, or attach the bound via the builder/object API instead.
- Remove quotes from numeric bounds ('5' → 5).
- If end-of-input, append the missing bound value after the comparator.
Example fix
// before
schema.parse(`number >= ${String(limit)}`); // limit is "high" -> throws
// after
schema.parse(`number >= ${Number(limit)}`); // or validate Number.isFinite(limit) first Defensive patterns
Strategy: validation
Validate before calling
function assertNumericBound(v) {
if (typeof v !== "number" || !Number.isFinite(v)) throw new Error(`bound must be a number, got ${v}`);
return String(v);
} Type guard
function isNumericLiteralToken(s) {
return /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/.test(s);
} Try / catch
try {
const schema = parse(def);
} catch (e) {
if (String(e.message).includes("expected bound after comparator")) {
// replace identifier/quoted bound with a plain numeric or date literal
}
throw e;
} Prevention
- Never interpolate identifiers or quoted values where a bound literal is required.
- Coerce and validate bounds with Number() before embedding them in definitions.
- Keep bounds as raw digits or d"..." date literals.
When it happens
Trigger: 'number > max' (identifier instead of literal), 'string > ', "number == '5'" (string literal where num/bigint/date required), 'number >' at end of input.
Common situations: Interpolating named constants into definitions ('number >= ${limit}' where limit is undefined or non-numeric); quoting numeric bounds by habit; truncation when the value part of the definition was lost.
Related errors
- Malformed number literal '${raw}'
- unexpected character '${c}' in "${src}"
- unexpected end of definition "${this.#src}"
- unsupported default literal in "${this.#src}"
- trailing tokens in definition "${this.#src}"
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a8a532e61b2b0c5d.
Report an issue: GitHub.