can1357/oh-my-pi · error · OmpTypeError
left bound requires a corresponding right bound in "${this.#
Error message
left bound requires a corresponding right bound in "${this.#src}" What it means
A left-bounded range ('lo < base' or 'lo <= base') was written, but after the base the parser did not find a comparison operator to close the range. Two-sided ranges like '0 < number < 10' must end with a right bound; a bare '0 < number' is rejected so an incomplete range never silently becomes one-sided.
Source
Thrown at packages/omptype/src/ir.ts:436
return { k: "intersection", members };
}
/**
* `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") {View on GitHub (pinned to 9690622007)
Solutions
- Complete the range: '0 < number < 100'.
- If only a lower bound is wanted, flip it to postfix form: 'number > 0' or 'number >= 0'.
- Check that the string wasn't truncated — append the missing ' < N' portion.
- Note that '>' / '>=' as the right comparator is also rejected (see the companion error); only '<' or '<=' may close the range.
Example fix
// before
schema.parse("0 < number");
// after
schema.parse("0 < number < 100"); // or "number > 0" Defensive patterns
Strategy: validation
Validate before calling
// left-bound form must contain a closing upper comparator
if (/^\s*-?\d+(?:\.\d+)?\s*[<=]\s*\S+/.test(def) && !/[<>]=?\s*-?\d+(?:\.\d+)?[^<>=]*$/.test(def)) {
// might be one-sided; prefer rewriting or completing the range
} Try / catch
try {
const schema = parse(def);
} catch (e) {
if (String(e.message).includes("left bound requires a corresponding right bound")) {
// rewrite as "base > lo" or append " < hi"
}
throw e;
} Prevention
- Prefer postfix bounds ('number > 0') unless you truly need a two-sided range.
- When using 'lo < base' form, always finish with '<' or '<=' and a numeric/date literal.
- Lint definitions for 'lo < base' patterns lacking a trailing bound.
When it happens
Trigger: Definitions like '0 < number' (missing ' < 10'), '0 < number[]', or '0 < string' followed by end-of-input or a non-comparator token.
Common situations: Thinking the left-bound form alone expresses a minimum (in omptype it must be completed on the right, or rewritten as 'number > 0'); truncation during string building; mixing syntax from other validators where '0 < number' is valid.
Related errors
- right bound must use < or <= in "${this.#src}"
- 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/aaba958f3dd2ab1b.
Report an issue: GitHub.