can1357/oh-my-pi · error · OmpTypeError
tuple length intersection is unsatisfiable
Error message
tuple length intersection is unsatisfiable
What it means
When intersecting two tuple types, omptype computes the length interval: the intersection's minimum is the max of both required-prefix lengths, its maximum the min of both maximum lengths. If minimum > maximum no tuple can satisfy both sides, so construction throws immediately instead of yielding an uninhabitable type.
Source
Thrown at packages/omptype/src/type.ts:1946
};
}
function intersectTuples(left: TupleIR, right: TupleIR): IR {
if (
left.postfix.length !== 0 ||
right.postfix.length !== 0 ||
left.prefix.some(item => item.hasDefault) ||
right.prefix.some(item => item.hasDefault)
) {
return { k: "intersection", members: [left, right] };
}
const leftRequired = left.prefix.filter(item => !item.opt).length;
const rightRequired = right.prefix.filter(item => !item.opt).length;
const minimum = Math.max(leftRequired, rightRequired);
const leftMaximum = left.variadic === undefined ? left.prefix.length : Number.POSITIVE_INFINITY;
const rightMaximum = right.variadic === undefined ? right.prefix.length : Number.POSITIVE_INFINITY;
const maximum = Math.min(leftMaximum, rightMaximum);
if (minimum > maximum) throw new OmpTypeError("tuple length intersection is unsatisfiable");
const prefixLength = Number.isFinite(maximum) ? maximum : Math.max(left.prefix.length, right.prefix.length);
const prefix: TupleIR["prefix"] = [];
for (let index = 0; index < prefixLength; index++) {
const leftItem = left.prefix[index];
const rightItem = right.prefix[index];
const leftNode = leftItem?.val ?? left.variadic;
const rightNode = rightItem?.val ?? right.variadic;
if (leftNode === undefined || rightNode === undefined) break;
const required = (leftItem !== undefined && !leftItem.opt) || (rightItem !== undefined && !rightItem.opt);
try {
prefix.push({ val: intersect(leftNode, rightNode), opt: !required });
} catch (error) {
if (required || !(error instanceof OmpTypeError)) throw error;
break;
}
}
const variadic =View on GitHub (pinned to 9690622007)
Solutions
- Align tuple lengths so the shorter side can accept the longer side's required elements (pad with optional items or add a variadic)
- Make trailing items optional: tuple([string, string, optional(number)]) instead of fixed length 3
- If variable length is intended, give one side a rest element (...number()) so its maximum is infinite
- Check which two schemas are being intersected; likely one is an outdated copy
Example fix
// before const A = tuple([string, string]); const B = tuple([string]); intersect(A, B) // throws // after const B = tuple([string, optional(string)]); intersect(A, B)
Defensive patterns
Strategy: try-catch
Validate before calling
function tupleLengthCompatible(a, b) {
const minA = a.prefix.filter(i => !i.opt).length;
const minB = b.prefix.filter(i => !i.opt).length;
const maxA = a.variadic !== undefined ? Infinity : a.prefix.length;
const maxB = b.variadic !== undefined ? Infinity : b.prefix.length;
return Math.max(minA, minB) <= Math.min(maxA, maxB);
} Try / catch
try {
return intersect(tupleA, tupleB);
} catch (err) {
if (err instanceof OmpTypeError && err.message.includes('tuple length')) {
logger.error('incompatible tuple lengths', { a: tupleA, b: tupleB });
return never();
}
throw err;
} Prevention
- Keep tuple definitions in one shared module so versions can't drift
- Make trailing tuple elements optional when lengths may vary
- Compute length compatibility before intersecting programmatically-derived tuples
When it happens
Trigger: intersect(tuple([string, string, string]), tuple([string])) — one tuple requires 3 elements while the other allows at most 1; similarly tuple([a, optional(b)]) ∩ tuple([a, b, c]).
Common situations: Version-drift between two tuple definitions (one grew a field, the other didn't); intersecting a fixed tuple with a shorter one by mistake; config schema and payload schema diverging after a code change.
Related errors
- intersection with never is unsatisfiable
- intersection of distinct morphs is indeterminate
- 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/c7922eda3d07037e.
Report an issue: GitHub.