can1357/oh-my-pi · error · OmpTypeError
intersection of distinct morphs is indeterminate
Error message
intersection of distinct morphs is indeterminate
What it means
Intersecting two morphs (types with in/out transformation functions, e.g. string coerced to number) is only defined when both sides are the same morph with the same output. Different functions or outputs would make the resulting input/output ordering ambiguous, so omptype refuses with 'indeterminate' rather than picking an arbitrary order.
Source
Thrown at packages/omptype/src/type.ts:2009
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)));
}
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;View on GitHub (pinned to 9690622007)
Solutions
- Use the identical morph instance/function on both sides of the intersection
- Pre-compose: apply one morph then refine with a plain predicate instead of intersecting two morphs
- Refactor so only one side performs the transformation and the other is a refinement of the output type
- If merging schemas (e.g. config + overrides), strip one side's morph before intersecting
Example fix
// before intersect(number(string), number(coerceBigint)) // after number(string).refine(n => Number.isSafeInteger(n))
Defensive patterns
Strategy: try-catch
Validate before calling
function morphsCompatible(a, b) {
return a.ir?.k !== 'morph' || b.ir?.k !== 'morph' || (a.ir.fn === b.ir.fn && a.ir.out === b.ir.out);
} Type guard
function isMorphSchema(t): boolean {
return t != null && t.ir?.k === 'morph';
} Try / catch
try {
return intersect(a, b);
} catch (err) {
if (err instanceof OmpTypeError && err.message.includes('morphs is indeterminate')) {
throw new Error('two different morphs on one field — pick a single transformation');
}
throw err;
} Prevention
- Share one morph instance for a given coercion across modules
- Prefer refine() over a second morph when combining constraints
- When merging schema sets, strip duplicate morphs from one side first
When it happens
Trigger: intersect(number(string), number(bigintString)) or intersect(morph(f), morph(g)) with f !== g; intersecting two 'sub' schemas backed by different step chains also lands here.
Common situations: Combining two independently defined coercion schemas (e.g. two different string→number parsers) in an allOf/intersection; library defaults plus user overrides both installing a morph on the same field.
Related errors
- tuple length intersection is unsatisfiable
- intersection with never is unsatisfiable
- union intersection invariant failed
- 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/dbb2bca4ada53e00.
Report an issue: GitHub.