{"record":{"id":"12e6f0ebb2584581","repo":"can1357/oh-my-pi","slug":"expected-literal-after-in-this-src","errorCode":null,"errorMessage":"expected literal after == in \"${this.#src}\"","messagePattern":"expected literal after == in \"(.+?)\"","errorType":"exception","errorClass":"OmpTypeError","httpStatus":null,"severity":"error","filePath":"packages/omptype/src/ir.ts","lineNumber":455,"sourceCode":"\t\t\t}\n\t\t\tif (t2.v === \">\" || t2.v === \">=\") {\n\t\t\t\tthrow new OmpTypeError(`right bound must use < or <= in \"${this.#src}\"`);\n\t\t\t}\n\t\t\tthis.#pos++;\n\t\t\tconst hi = this.#next();\n\t\t\tif (hi.t !== \"num\" && hi.t !== \"date\") {\n\t\t\t\tthrow new OmpTypeError(`expected bound after comparator in \"${this.#src}\"`);\n\t\t\t}\n\t\t\tnode = applyBound(node, t2.v, hi.v, this.#src);\n\t\t\treturn this.#eatArraySuffixes(node);\n\t\t}\n\t\tlet node = this.#eatDivisor(this.parsePostfix());\n\t\tconst t2 = this.#peek();\n\t\tif (t2?.t === \"op\" && t2.v === \"==\") {\n\t\t\tthis.#pos++;\n\t\t\tconst limit = this.#next();\n\t\t\tif (limit.t !== \"num\" && limit.t !== \"bigint\" && limit.t !== \"date\") {\n\t\t\t\tthrow new OmpTypeError(`expected literal after == in \"${this.#src}\"`);\n\t\t\t}\n\t\t\tnode = applyEquality(node, limit.v, this.#src);\n\t\t} else if (t2?.t === \"op\" && CMP[t2.v] && (this.#peek(1)?.t === \"num\" || this.#peek(1)?.t === \"date\")) {\n\t\t\tthis.#pos++;\n\t\t\tconst limit = this.#next() as Extract<Tok, { t: \"num\" | \"date\" }>;\n\t\t\tnode = applyBound(node, t2.v, limit.v, this.#src);\n\t\t\tnode = this.#eatArraySuffixes(node);\n\t\t}\n\t\treturn node;\n\t}\n\n\t#eatDivisor(node: IR): IR {\n\t\tif (!this.#eatOp(\"%\")) return node;\n\t\tconst divisor = this.#next();\n\t\tif (divisor.t !== \"num\") throw new OmpTypeError(`expected number after % in \"${this.#src}\"`);\n\t\tif (node.k !== \"number\") throw new OmpTypeError(`% requires number in \"${this.#src}\"`);\n\t\tif (!Number.isFinite(divisor.v) || !Number.isInteger(divisor.v) || divisor.v === 0)\n\t\t\tthrow new OmpTypeError(`divisor must be a non-zero integer in \"${this.#src}\"`);","sourceCodeStart":437,"sourceCodeEnd":473,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/omptype/src/ir.ts#L437-L473","documentation":"The '==' equality constraint requires a num, bigint, or date literal immediately after it, but another token kind appeared. Unlike bounds, '==' does not accept date-vs-num mixing excuses — the operand must be one of those literal token types, so identifiers, strings, booleans, and end-of-input all throw.","triggerScenarios":"'number == x' (identifier), \"'a' == 'b'\" where the right side is fine but 'number == \"5\"' (string token) fails, 'number ==' at end of input, 'boolean == 1'.","commonSituations":"Interpolating variable names into definitions expecting them to resolve at runtime; quoting the equality operand; building 'field == value' strings where value was never stringified into a literal.","solutions":["Use a literal operand: 'number == 5', \"'a' == 'a'\", 'bigint == 10n', 'Date == d\"2024-01-01\"'.","Stringify numbers before interpolation and never quote them; validate the interpolated value with Number.isFinite/typeof first.","For non-numeric equality, use the appropriate literal syntax (quoted string, true/false keyword) or the corresponding keyword type instead of '=='.","If the operand is dynamic, build the schema via the object/IR API where values are passed as data, not parsed text."],"exampleFix":"// before\nschema.parse(`number == ${value}`); // value = \"x\" -> throws\n// after\nif (!Number.isFinite(value)) throw new Error(\"value must be numeric\");\nschema.parse(`number == ${value}`);","handlingStrategy":"validation","validationCode":"function assertEqualityOperand(v) {\n  const ok = typeof v === \"number\" || typeof v === \"bigint\" || v instanceof Date;\n  if (!ok) throw new Error(`== operand must be num/bigint/date literal, got ${typeof v}`);\n}","typeGuard":"function isEqualityLiteral(v) {\n  return typeof v === \"number\" || typeof v === \"bigint\" || v instanceof Date;\n}","tryCatchPattern":"try {\n  const schema = parse(def);\n} catch (e) {\n  if (String(e.message).includes(\"expected literal after ==\")) {\n    // coerce the operand to a literal or switch to the value-based API\n  }\n  throw e;\n}","preventionTips":["Only place numeric, bigint (n-suffixed), or date literals after '=='.","Validate interpolated equality operands are finite numbers before composing the string.","For string/boolean equality, use quoted literals or true/false keywords in the appropriate slot instead."],"tags":["parser","equality","schema-definition"],"backgroundTag":"missing-bound-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}