can1357/oh-my-pi · error · OmpTypeError
right bound must use < or <= in "${this.#src}"
Error message
right bound must use < or <= in "${this.#src}" What it means
In a two-sided range 'lo CMP base CMP2 hi', the right comparator must be '<' or '<=' — writing '0 < number > 10' or '0 < number >= 10' is contradictory (cannot be satisfied) and is rejected at parse time. The left side already establishes the lower bound with flipped semantics, so only upper-bound comparators are legal on the right.
Source
Thrown at packages/omptype/src/ir.ts:439
/**
* `NUM CMP base (CMP NUM)?` or `base (CMP NUM)?`, with `[]*` postfix on the
* base AND after a trailing bound — `string>0[]` is an array of bounded
* strings, matching ArkType precedence (bounds bind tighter than `[]`).
*/
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);View on GitHub (pinned to 9690622007)
Solutions
- Use '<' or '<=' for the right comparator: '0 < number < 10'.
- Express the intended (possibly odd) constraint as an intersection of two one-sided bounds if truly needed: 'number > 0 & number > 10' — though verify it isn't contradictory.
- Re-read the range in reading order: left value is the minimum, right value is the maximum.
- Validate programmatically-built ranges so lo <= hi and comparators are generated, not hand-typed.
Example fix
// before
schema.parse("0 < number > 10");
// after
schema.parse("0 < number < 10"); Defensive patterns
Strategy: validation
Validate before calling
// in a two-sided range the right comparator must be < or <=
const m = def.match(/^\s*-?\d+(?:\.\d+)?\s*[<=]\s*\S+\s*([<>=]+)/);
if (m && (m[1] === ">" || m[1] === ">=")) throw new Error(`wrong right comparator ${m[1]} in: ${def}`); Try / catch
try {
const schema = parse(def);
} catch (e) {
if (String(e.message).includes("right bound must use < or <=")) {
// flip the right comparator to < or <=
}
throw e;
} Prevention
- Read two-sided ranges as 'min < base < max' and keep both comparators pointing right.
- Never chain '>' after a left bound; express independent lower bounds as separate intersections.
- Generate ranges programmatically to keep comparator direction consistent.
When it happens
Trigger: Definitions like '0 < number > 10' or '1 <= number >= 5' where the second comparator points the wrong way.
Common situations: Transliterating chained comparisons from prose ('greater than 0 and greater than 10'); typos when editing both bounds in the same direction; copying a lower-bound expression into the right-hand slot.
Related errors
- left bound requires a corresponding right bound in "${this.#
- expected bound after comparator in "${this.#src}"
- No files were modified.
- The first line of the patch must be '*** Begin Patch'
- The last line of the patch must be '*** End Patch'
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/287a70ab7357c833.
Report an issue: GitHub.