can1357/oh-my-pi · error · OmpTypeError

intersection with never is unsatisfiable

Error message

intersection with never is unsatisfiable

What it means

intersectResolved() treats a never operand as fatal: never is the empty type, and omptype's intersection compiler does not model the (mathematically valid but useless) never result, so it throws to surface the mistake at schema-build time. If you legitimately want never, construct it explicitly rather than intersecting into it.

Source

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

		target[kIntersections] ??= new WeakMap<IR, IR>();
		const cache = target[kIntersections];
		const existing = cache.get(b);
		if (existing !== undefined) return existing;
		let resolved: IR | undefined;
		const reference: IR = {
			k: "alias",
			name: a.k === "alias" ? a.name : b.k === "alias" ? b.name : "intersection",
			resolve: () =>
				(resolved ??= intersectResolved(a.k === "alias" ? a.resolve() : a, b.k === "alias" ? b.resolve() : b)),
		};
		cache.set(b, reference);
		return reference;
	}
	return intersectResolved(a, b);
}

function intersectResolved(a: IR, b: IR): IR {
	if (a.k === "never" || b.k === "never") throw new OmpTypeError("intersection with never is unsatisfiable");
	if (a.k === "unknown") return b;
	if (b.k === "unknown") return a;
	if (a === b) return a;
	if (a.k === "morph" && b.k === "morph") {
		if (a.fn !== b.fn || a.out !== b.out) {
			throw new OmpTypeError("intersection of distinct morphs is indeterminate");
		}
		return { ...a, input: intersect(a.input, b.input) };
	}
	if (a.k === "morph") return { ...a, input: intersect(a.input, b) };
	if (b.k === "morph") return { ...b, input: intersect(a, b.input) };
	if (a.k === "sub" && a.schema.hasSteps) {
		if (b.k === "sub" && b.schema.hasSteps) {
			if (a.schema === b.schema) return a;
			throw new OmpTypeError("intersection of distinct morphs is indeterminate");
		}
		const schema = a.schema as InternalType;
		return embed(makeType(intersect(schema.ir, b), schema[kSteps], metaOf(schema)));

View on GitHub (pinned to 9690622007)

Solutions

  1. Remove the never operand — the intersection is vacuous; just use the other type or never() directly
  2. If one side comes from a generic/union, short-circuit: if (schema.isNever) return never()
  3. Check where the never-typed value leaked from (usually an empty array inferred as never[] or an empty union)

Example fix

// before
const T = intersect(configured, never())
// after
const T = configured // or never() if emptiness is intended
Defensive patterns

Strategy: validation

Validate before calling

function safeIntersect(a, b) {
  if (a.isNever || b.isNever) return never();
  return intersect(a, b);
}

Type guard

function isNeverSchema(t): boolean {
  return t != null && t.ir?.k === 'never';
}

Try / catch

try {
  return intersect(a, b);
} catch (err) {
  if (err instanceof OmpTypeError && err.message.includes('never')) {
    return never(); // intersection with never is vacuously never
  }
  throw err;
}

Prevention

When it happens

Trigger: intersect(never(), string), or an intersection where one argument's IR resolved to never (e.g. an alias to never or an empty construct).

Common situations: Guarding 'impossible' branches by intersecting with never; a generic helper that receives never from an empty union; refactoring that accidentally aliases a schema to never().

Related errors


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