block/buzz · error · Error

The mention @${winners[0].displayName} is ambiguous. Choose

Error message

The mention @${winners[0].displayName} is ambiguous. Choose a recipient from the mention picker.

What it means

extractMentionPubkeys resolves typed @Name mentions to pubkeys. When the winning display-name matches multiple distinct identities (identities.size > 1), the mention is ambiguous — the code throws rather than silently fanning out to all same-named identities, per the mention editor contract.

Source

Thrown at desktop/src/features/messages/lib/extractMentionPubkeys.ts:135

/** Extract recipients from the same exact occurrences used by draft routing. */
export function extractMentionPubkeys(options: {
  text: string;
  selectedMentions: ReadonlyMap<string, string>;
  selectedDisplayNames?: Iterable<string>;
  competingDisplayNames?: Iterable<string>;
  memberCandidates: readonly MentionPubkeyCandidate[];
}): string[] {
  const { text, selectedMentions, memberCandidates } = options;
  const candidates = mentionMatchCandidates(options);
  const winningPubkeys = new Set<string>();
  for (const { candidates: winners } of mentionOccurrences(text, candidates)) {
    const identities = new Set(
      winners.flatMap((match) =>
        match.pubkey ? [match.pubkey.toLowerCase()] : [],
      ),
    );
    if (identities.size > 1) {
      throw new Error(
        `The mention @${winners[0].displayName} is ambiguous. Choose a recipient from the mention picker.`,
      );
    }
    for (const match of winners) {
      if (match.pubkey) winningPubkeys.add(match.pubkey);
    }
  }

  const pubkeys: string[] = [];
  for (const [, pubkey] of selectedMentions) {
    if (winningPubkeys.delete(pubkey)) pubkeys.push(pubkey);
  }
  for (const candidate of memberCandidates) {
    if (candidate.pubkey && winningPubkeys.delete(candidate.pubkey)) {
      pubkeys.push(candidate.pubkey);
    }
  }
  return pubkeys;

View on GitHub (pinned to dad5a33865)

Solutions

  1. Re-enter the mention via the mention picker (autocomplete) so the exact pubkey is bound and no ambiguity check runs.
  2. Rename one of the colliding identities (or use its distinguishing handle) so typed names resolve uniquely.
  3. Remove or rewrite the ambiguous mention text in the draft.
  4. If the collision is stale, refresh the directory so duplicates that no longer exist disappear.

Example fix

// before
const pubkeys = extractMentionPubkeys(content); // throws on '@Sam' with two Sams

// after
try {
  const pubkeys = extractMentionPubkeys(content);
} catch (e) {
  if (String(e?.message).includes("is ambiguous")) {
    openMentionPicker(); // force explicit recipient selection
    return null;
  }
  throw e;
}
Defensive patterns

Strategy: validation

Validate before calling

const matches = directory.lookupByName(name);
if (new Set(matches.map(m => m.pubkey?.toLowerCase()).filter(Boolean)).size > 1) {
  openMentionPicker(name); // force explicit disambiguation
}

Type guard

function isUnambiguousName(name: string, dir: Directory): boolean {
  const ids = new Set(
    dir.lookupByName(name).flatMap(m => (m.pubkey ? [m.pubkey.toLowerCase()] : [])),
  );
  return ids.size <= 1;
}

Try / catch

try {
  const { pubkeys } = extractMentionPubkeys(body);
} catch (e) {
  if (e instanceof Error && e.message.includes("is ambiguous")) {
    highlightAmbiguousMentionInDraft();
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: A message body contains @Name where two or more registered identities (teammates, agents, automatic addresses) share that exact displayName, and the text was typed manually rather than picked from the mention picker.

Common situations: Two teammates with the same display name joined the community; a user and an agent share a name; a message was pasted or edited so the mention text no longer carries its original pubkey binding.

Related errors


AI-assisted analysis of block/buzz@dad5a33865 (2026-09-05). Data as JSON: /api/errors/21a631121afa5525. Report an issue: GitHub.