can1357/oh-my-pi · error · OmpTypeError

unsupported default literal in "${this.#src}"

Error message

unsupported default literal in "${this.#src}"

What it means

After a '= default' marker, parseTop only accepts literal tokens (num, bigint, date, str) or the identifiers true/false/null/undefined. Any other token (a keyword like 'string', an identifier, an operator) is rejected because defaults must be compile-time literals, not computed expressions or type references.

Source

Thrown at packages/omptype/src/ir.ts:376

		if (t?.t === "op" && t.v === v) {
			this.#pos++;
			return true;
		}
		return false;
	}

	/** Full definition with optional trailing `= literal` default and/or `?` optional marker. */
	parseTop(): ParsedTop {
		const ir = this.parseUnion();
		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());

View on GitHub (pinned to 9690622007)

Solutions

  1. Quote string defaults: "string='abc'" not 'string=abc'.
  2. Use only literal forms after '=': numbers, 5n-style bigints, dates, quoted strings, true/false/null/undefined.
  3. Compute the value in code and supply it through the API's runtime-default mechanism if a non-literal default is needed.
  4. Check ordering: the '=' clause follows the whole union, so ensure the default token itself is a literal, not an expression.

Example fix

// before
schema.parse("string=hello");
// after
schema.parse("string='hello'");
Defensive patterns

Strategy: validation

Validate before calling

function isValidDefault(d) {
  return typeof d === "number" || typeof d === "bigint" || d instanceof Date ||
    typeof d === "string" || typeof d === "boolean" || d === null || d === undefined;
}

Type guard

function isLiteralDefault(v) {
  return v === null || v === undefined || typeof v === "boolean" ||
    typeof v === "number" || typeof v === "bigint" || typeof v === "string" || v instanceof Date;
}

Try / catch

try {
  const schema = parse(def);
} catch (e) {
  if (String(e.message).includes("unsupported default literal")) {
    // quote string defaults or supply the default via the runtime API
  }
  throw e;
}

Prevention

When it happens

Trigger: Definitions like 'string=hello' (unquoted string), 'number=Math.PI', "string='a' | 'b'=..." with a non-literal after '=', or 'boolean=yes'.

Common situations: Writing defaults for string types without quotes ('string=abc' instead of "string='abc'"); referencing constants by name expecting runtime evaluation; pasting TS default-value syntax ('number = 5' with spaces is fine, but 'number=default' is not).

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/a382539119537860. Report an issue: GitHub.