can1357/oh-my-pi · critical · AmbiguousOverlapError
ambiguous overlap for `${provider}/${model}` on axis `${axis
Error message
ambiguous overlap for `${provider}/${model}` on axis `${axis}`: rules `${first}` and `${second}` tie; add an explicit priority What it means
AmbiguousOverlapError thrown by contest() during resolveOverIndex when two compatibility rules with the same rank both match a provider/model target for the same axis. The rule tree cannot decide a winner, so resolution fails fast instead of silently picking one. The message tells you exactly which two rule sources tie.
Source
Thrown at packages/catalog/src/compat/cascade.ts:155
function rankCompare(a: readonly [number, number, number], b: readonly [number, number, number]): number {
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
}
function contest(
winners: WinnerTable,
axes: Record<string, unknown> | undefined,
rank: readonly [number, number, number],
rule: IndexedRule,
target: ResolveTarget,
): void {
if (!axes) return;
for (const axis in axes) {
const held = winners[axis];
if (held) {
const order = rankCompare(held.rank, rank);
if (order === 0) {
throw new AmbiguousOverlapError(
target.provider,
target.model,
axis,
held.rule.compiled.source,
rule.compiled.source,
);
}
if (order > 0) continue;
}
winners[axis] = { rank, rule };
}
}
function collect(winners: WinnerTable, pick: (rule: CompiledRule) => Record<string, unknown> | undefined) {
const out: Record<string, unknown> = {};
for (const axis in winners) {
out[axis] = pick(winners[axis].rule.compiled)?.[axis];
}View on GitHub (pinned to 9690622007)
Solutions
- Open the two rule sources named in the message and add `priority=` to the one that should win.
- Alternatively narrow one rule's selector (more specific class/provider/family) so ranks no longer tie.
- Run `bun run gen:compat` after the KDL edit and verify resolution with a lookup of the affected model id.
Example fix
// before (providers/foo.kdl)
rule "gpt-5 thinking" { providers "foo"; efforts ... }
// after: break the tie explicitly
rule "gpt-5 thinking" { providers "foo"; priority 10; efforts ... } Defensive patterns
Strategy: validation
Validate before calling
null
Type guard
null
Try / catch
try {
const resolved = resolveModelPolicy(provider, model);
} catch (err) {
if (err instanceof AmbiguousOverlapError) {
// err names the axis and the two tied rule sources
logger.error("ambiguous compat rules", { model, axis: err.axis });
}
throw err;
} Prevention
- Add explicit `priority=` whenever a new KDL rule could overlap an existing one on the same axis.
- Keep classes/*.kdl rules lineage-only and providers/*.kdl rules deployment-only to reduce overlap.
- Run the compat test suite after adding rules; equal-rank overlaps throw at resolve time in tests too.
When it happens
Trigger: resolveModelPolicy / cascade resolution where two KDL rules (e.g. a class rule and a providers rule of equal rank) both define the same axis (e.g. thinking efforts) for the same model and neither has an explicit `priority=`.
Common situations: Adding a new providers/*.kdl rule that overlaps an existing classes/*.kdl rule for a model; splitting a broad rule into two without priority hints; upstream catalog additions making a wildcard rule collide with an exact one of equal rank.
Related errors
- invalid compiled revision term in ${compiled.source}
- Model ${spec.provider}/${spec.id} resolved to an empty think
- ambiguous family for `${model}`: `${tied[0]}` and `${tied[1]
- ambiguous class for `${lower}`: `${tied[0]}` and `${tied[1]}
- missing reviewed collapse table for google-gemini-cli
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/8457f5cc03d7e771.
Report an issue: GitHub.