can1357/oh-my-pi · error · OmpTypeError
union intersection invariant failed
Error message
union intersection invariant failed
What it means
In intersectResolved(), after detecting that at least one operand is a union, the code recomputes which operand was the union; if neither branch actually has k === 'union' despite the earlier guard, an internal invariant is broken and omptype throws. This is effectively an assertion — hitting it means corrupt or non-normalized IR reached the intersection (e.g. a hand-built IR or a failed alias resolution).
Source
Thrown at packages/omptype/src/type.ts:2026
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)));
}
if (b.k === "sub" && b.schema.hasSteps) return intersect(b, a);
if (a.k === "union" || b.k === "union") {
const union = a.k === "union" ? a : b.k === "union" ? b : undefined;
if (union === undefined) throw new OmpTypeError("union intersection invariant failed");
const branches = union.members;
const other = a.k === "union" ? b : a;
const members: IR[] = [];
for (const branch of branches) {
try {
members.push(intersect(branch, other));
} catch (error) {
if (!(error instanceof OmpTypeError)) throw error;
}
}
if (members.length === 0) throw new OmpTypeError("intersection has no satisfiable branches");
return members.length === 1 ? members[0] : { k: "union", members };
}
if (a.k === "lit") {
if (walk(b, a.v) instanceof OmpErrors) throw new OmpTypeError("literal is excluded by the intersection");
return a;
}
if (b.k === "lit") return intersect(b, a);View on GitHub (pinned to 9690622007)
Solutions
- Deduplicate omptype so only one version exists (bun pm ls | grep omptype; fix duplicate installs)
- Never construct or mutate IR objects directly — only use public builders
- Rebuild the offending schema with current public API instead of copying cached/deserialized IR
- If reproducible with public API only, report it as a bug with a minimal repro
Example fix
// before
// manually mutated schema.ir to a union-like object
schema.ir = { k: 'union', members: [...] }
// after
const T = union(memberA, memberB) // build via API
intersect(T, other) Defensive patterns
Strategy: try-catch
Validate before calling
// ensure operands come from the public API and one package version:
// bun pm ls | grep omptype → expect exactly one version
function isWellFormedIR(ir) {
return ir && typeof ir.k === 'string' && (ir.k !== 'union' || Array.isArray(ir.members));
} Type guard
function isUnionIR(ir): ir is { k: 'union'; members: unknown[] } {
return typeof ir === 'object' && ir !== null && ir.k === 'union' && Array.isArray(ir.members);
} Try / catch
try {
return intersect(a, b);
} catch (err) {
if (err instanceof OmpTypeError && err.message.includes('union intersection invariant')) {
logger.error('omptype IR invariant violated — likely version skew or manual IR mutation');
throw err; // do not swallow; fix the duplicate-install or rebuild the schema
}
throw err;
} Prevention
- Never construct or mutate IR objects manually
- Deduplicate omptype in node_modules to avoid mixed IR shapes
- If it reproduces with only public API, file a bug with a minimal repro
When it happens
Trigger: Feeding a custom/foreign IR object into embed/intersect; an alias resolving to something mutated after construction; a bug or version mismatch inside omptype's IR normalization.
Common situations: Patching IR internals or monkey-patching schema objects; mixing omptype versions where IR shapes changed (e.g. two copies of the package in node_modules); deserializing schemas across versions.
Related errors
- tuple length intersection is unsatisfiable
- intersection with never is unsatisfiable
- intersection of distinct morphs is indeterminate
- intersection has no satisfiable branches
- literal is excluded by the intersection
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/5f28678d9cc842da.
Report an issue: GitHub.