can1357/oh-my-pi · error · OmpTypeError

intersection of ${leftDomain} and ${rightDomain} is unsatisf

Error message

intersection of ${leftDomain} and ${rightDomain} is unsatisfiable

What it means

Generic domain-conflict check near the end of the intersection algorithm: if both operands have a defined value domain (from domainOf) and the domains differ, no value can belong to both, so the intersection is rejected. This is the catch-all for kind combinations not handled by the earlier structural branches.

Source

Thrown at packages/omptype/src/type.ts:2138

	if (a.k === "tuple" && b.k === "tuple") return intersectTuples(a, b);
	if (a.k === "tuple" && b.k === "array") return intersectTupleWithArray(a, b);
	if (a.k === "array" && b.k === "tuple") return intersectTupleWithArray(b, a);
	if (a.k === "instance" && b.k === "instance") {
		if (a.ctor === b.ctor || a.ctor.prototype instanceof b.ctor) return a;
		if (b.ctor.prototype instanceof a.ctor) return b;
		throw new OmpTypeError(`intersection of ${a.expected} and ${b.expected} is unsatisfiable`);
	}
	if (a.k === b.k && ["null", "undefined", "boolean", "bigint", "symbol", "anyobject"].includes(a.k)) return a;
	if (
		(a.k === "object" && (b.k === "array" || b.k === "tuple")) ||
		(b.k === "object" && (a.k === "array" || a.k === "tuple"))
	) {
		return { k: "intersection", members: [a, b] };
	}
	const leftDomain = domainOf(a);
	const rightDomain = domainOf(b);
	if (leftDomain !== undefined && rightDomain !== undefined && leftDomain !== rightDomain) {
		throw new OmpTypeError(`intersection of ${leftDomain} and ${rightDomain} is unsatisfiable`);
	}
	if (a.k === "anyobject" && rightDomain === "object") return b;
	if (b.k === "anyobject" && leftDomain === "object") return a;
	const members = [...(a.k === "intersection" ? a.members : [a]), ...(b.k === "intersection" ? b.members : [b])];
	return { k: "intersection", members };
}

/** Reduce parsed unions/intersections to their observable semantic form. */
function normalizeIR(ir: IR): IR {
	switch (ir.k) {
		case "intersection": {
			const members = ir.members.map(normalizeIR);
			if (members.length === 0) return { k: "unknown" };
			return members.slice(1).reduce(intersect, members[0]);
		}
		case "union": {
			const members: IR[] = [];
			let changed = false;

View on GitHub (pinned to 9690622007)

Solutions

  1. Ensure both operands share the same base domain before intersecting
  2. Inspect each operand with the library's type inspection to confirm the intended kinds
  3. Fix operand ordering/arguments if a mixup passed the wrong type
  4. Catch OmpTypeError and log both expected domains to identify the mismatch

Example fix

// before
intersect(string(), number());
// after
intersect(string().url(), string().min(1));
Defensive patterns

Strategy: validation

Validate before calling

// compare kinds before intersecting
function domainsCompatible(a, b) {
  const da = domainOf(a), db = domainOf(b);
  return da === undefined || db === undefined || da === db;
}

Type guard

const isOmpTypeError = (e: unknown): e is OmpTypeError => e instanceof OmpTypeError;

Try / catch

try {
  const t = intersect(a, b);
} catch (err) {
  if (err instanceof OmpTypeError && err.message.includes('domain')) {
    logger.warn('intersecting incompatible domains', { left: describe(a), right: describe(b) });
  } else throw err;
}

Prevention

When it happens

Trigger: intersect(string(), number()); intersect(lit(1), string()); intersecting any two types whose domains (e.g. 'string' vs 'number', 'object' vs 'string') disagree and have no earlier special case.

Common situations: Wiring the wrong schema field into an intersection (name/positional mixup); a refactor changed one operand's base kind; intersecting an optional/undefined-domain type with a string-constrained type.

Related errors


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