can1357/oh-my-pi · critical · AmbiguousIdentityError

ambiguous class for `${lower}`: `${tied[0]}` and `${tied[1]}

Error message

ambiguous class for `${lower}`: `${tied[0]}` and `${tied[1]}` tie

What it means

AmbiguousIdentityError from classifyRanks: two class selectors in the taxonomy match a lowercased model id with equal rank, so class classification is indeterminate. Lenient mode degrades to class "unknown"; strict mode throws, naming both tied classes. Family and revision are only extracted after a unique class winner exists.

Source

Thrown at packages/catalog/src/compat/taxonomy.ts:155

	const lower = model.trim().toLowerCase();
	const bare = bareOf(lower);
	let winner: { rank: readonly [number, number]; cls: CompiledClass } | undefined;
	let tied: readonly [string, string] | undefined;
	for (const cls of rules.taxonomy.classes) {
		for (const matcher of cls.matchers) {
			if (!matcherMatches(matcher, lower, bare)) continue;
			const rank = [MATCHER_RANK[matcher.kind], matcher.token.length] as const;
			if (winner && winner.rank[0] === rank[0] && winner.rank[1] === rank[1] && winner.cls.id !== cls.id) {
				tied = [winner.cls.id, cls.id];
			} else if (!winner || winner.rank[0] < rank[0] || (winner.rank[0] === rank[0] && winner.rank[1] < rank[1])) {
				winner = { rank, cls };
				tied = undefined;
			}
		}
	}
	if (tied) {
		if (lenient) return { class: "unknown" };
		throw new AmbiguousIdentityError(lower, tied[0], tied[1], "class");
	}
	if (!winner) return { class: "unknown" };
	const ranks: ClassRanks = { class: winner.cls.id };
	const family = classifyFamily(winner.cls, bare, lower, lenient);
	if (family !== undefined) ranks.family = family;
	const revision = extractRevision(winner.cls, bare);
	if (revision !== undefined) ranks.revision = revision;
	return ranks;
}

function ranksInClass(classId: string, model: string, lenient: boolean): Omit<ClassRanks, "class"> {
	const cls = rules.taxonomy.classes.find(candidate => candidate.id === classId);
	if (!cls) return {};
	const lower = model.trim().toLowerCase();
	const bare = bareOf(lower);
	const out: Omit<ClassRanks, "class"> = {};
	const family = classifyFamily(cls, bare, lower, lenient);
	if (family !== undefined) out.family = family;

View on GitHub (pinned to 9690622007)

Solutions

  1. Differentiate the two class patterns so only one matches the colliding id.
  2. Add an explicit rank/priority to one class rule to break the tie.
  3. Regenerate via `bun run gen:compat` and verify classification of the previously ambiguous id.

Example fix

// before: "claude" and "claude-opus" classes tie on "claude-opus-4"
class "claude" { match "claude" }
// after: exclude the specific ids from the broad class
class "claude" { match "claude"; skip "claude-opus" }
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

try {
  const ranks = classifyRanks(lower, /* lenient */ false);
} catch (err) {
  if (err instanceof AmbiguousIdentityError && err.kind === "class") {
    return { class: "unknown" };
  }
  throw err;
}
// or pass lenient=true to get { class: "unknown" } instead of throwing

Prevention

When it happens

Trigger: cls()/ranks() on a model id where two class patterns (e.g. a generic "openai" class and a specific "o-series" class) both match with the same rank and neither is more specific.

Common situations: Introducing a new class KDL rule with a pattern that also matches ids claimed by an existing class; wildcard classes catching specific ids at equal rank.

Related errors


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