can1357/oh-my-pi · error · OmpTypeError
trailing tokens in definition "${this.#src}"
Error message
trailing tokens in definition "${this.#src}" What it means
After parsing a complete definition (union, optional '?', default), #expectEnd found leftover tokens. The string is grammatically valid up to a point but continues with content the top-level grammar doesn't accept — a full definition must consume the entire token stream.
Source
Thrown at packages/omptype/src/ir.ts:386
let def: unknown;
let hasDefault = false;
if (this.#eatOp("=")) {
const t = this.#next();
if (t.t === "num" || t.t === "bigint" || t.t === "date" || t.t === "str") def = t.v;
else if (t.t === "id" && (t.v === "true" || t.v === "false")) def = t.v === "true";
else if (t.t === "id" && t.v === "null") def = null;
else if (t.t === "id" && t.v === "undefined") def = undefined;
else throw new OmpTypeError(`unsupported default literal in "${this.#src}"`);
hasDefault = true;
}
const optional = this.#eatOp("?");
this.#expectEnd();
return { ir, def, hasDefault, optional };
}
#expectEnd(): void {
if (this.#pos < this.#toks.length) {
throw new OmpTypeError(`trailing tokens in definition "${this.#src}"`);
}
}
parseUnion(): IR {
const first = this.parseIntersection();
if (!this.#eatOp("|")) return first;
const members = [first, this.parseIntersection()];
while (this.#eatOp("|")) members.push(this.parseIntersection());
return { k: "union", members };
}
parseIntersection(): IR {
const first = this.parseBounded();
if (!this.#eatOp("&")) return first;
const members = [first, this.parseBounded()];
while (this.#eatOp("&")) members.push(this.parseBounded());
const literal = members.find((member): member is Extract<IR, { k: "lit" }> => member.k === "lit");
if (literal && typeof literal.v === "number") {View on GitHub (pinned to 9690622007)
Solutions
- Join alternatives with '|' ('string | number'), not whitespace.
- Remove stray separators (commas, semicolons, spaces) that aren't part of the grammar.
- If multiple properties are intended, wrap them in object syntax '{ key: value, ... }' rather than listing definitions sequentially.
- Read the echoed source in the message; everything after the valid definition is the offending tail.
Example fix
// before
schema.parse("string number");
// after
schema.parse("string | number"); Defensive patterns
Strategy: validation
Validate before calling
// tokenize-like sanity: no two adjacent keywords without a separator
if (/\b(string|number|boolean|bigint)\s+(string|number|boolean|bigint)\b/.test(def)) {
throw new Error(`missing '|' between types: ${def}`);
} Try / catch
try {
const schema = parse(def);
} catch (e) {
if (String(e.message).includes("trailing tokens")) {
// inspect everything after the first valid type; join with '|' or remove
}
throw e;
} Prevention
- Separate union members with '|', never whitespace or commas.
- When joining definitions programmatically, use an explicit separator and trim stray commas/semicolons.
- Put multi-property schemas in object syntax '{ ... }'.
When it happens
Trigger: 'string string' (two keywords), 'number 5', 'string[] number', stray commas/semicolons ('string,'), 'number>=1 2', or an extra '?'/'?' after content that parseTop already handled.
Common situations: Concatenating multiple definitions with a space instead of '|' ('string number' vs 'string | number'); leftover separator characters when joining definitions programmatically; typos where a comma was used instead of '|' in object properties.
Related errors
- Malformed number literal '${raw}'
- unexpected character '${c}' in "${src}"
- unexpected end of definition "${this.#src}"
- unsupported default literal in "${this.#src}"
- expected bound after comparator in "${this.#src}"
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2ffe93a76301ef3b.
Report an issue: GitHub.