paperclipai/paperclip · error · Error

Selector '${selector}' is ambiguous (matches both an ID and

Error message

Selector '${selector}' is ambiguous (matches both an ID and a shortname). Re-run with --by id or --by prefix.

What it means

Thrown by resolveCompanyForDeletion in auto mode when the selector matches both a company id and a different company's issue prefix. Without --by, the resolver cannot decide which company to target, so it refuses and asks the caller to disambiguate.

Source

Thrown at cli/src/commands/client/company.ts:1300

  const idMatch = companies.find((company) => company.id === selector);
  const prefixMatch = companies.find((company) => matchesPrefix(company, selector));

  if (by === "id") {
    if (!idMatch) {
      throw new Error(`No company found by ID '${selector}'.`);
    }
    return idMatch;
  }

  if (by === "prefix") {
    if (!prefixMatch) {
      throw new Error(`No company found by shortname/prefix '${selector}'.`);
    }
    return prefixMatch;
  }

  if (idMatch && prefixMatch && idMatch.id !== prefixMatch.id) {
    throw new Error(
      `Selector '${selector}' is ambiguous (matches both an ID and a shortname). Re-run with --by id or --by prefix.`,
    );
  }

  if (idMatch) return idMatch;
  if (prefixMatch) return prefixMatch;

  throw new Error(
    `No company found for selector '${selector}'. Use company ID or issue prefix (for example PAP).`,
  );
}

export function assertDeleteConfirmation(company: Company, opts: CompanyDeleteOptions): void {
  if (!opts.yes) {
    throw new Error("Deletion requires --yes.");
  }

  const confirm = opts.confirm?.trim();

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Re-run with '--by id' to target the company whose id matches.
  2. Re-run with '--by prefix' to target the company whose issue prefix matches.
  3. Use a longer/unique selector (full company id) to avoid the collision entirely.
  4. If collisions are frequent, consider renaming a prefix to avoid overlap.

Example fix

# before
paperclipai company delete PAP --yes --confirm PAP   # ambiguous
# after
paperclipai company delete PAP --by prefix --yes --confirm PAP
Defensive patterns

Strategy: validation

Validate before calling

function detectAmbiguousSelector(companies: { id: string; issuePrefix: string }[], selector: string): void {
  const idMatch = companies.find((c) => c.id === selector);
  const prefixMatch = companies.find((c) => c.issuePrefix.toUpperCase() === selector.toUpperCase());
  if (idMatch && prefixMatch && idMatch.id !== prefixMatch.id) {
    throw new Error(`Selector '${selector}' is ambiguous. Specify --by id or --by prefix.`);
  }
}

Type guard

function selectorIsUnambiguous(companies: { id: string; issuePrefix: string }[], selector: string): boolean {
  const idMatch = companies.find((c) => c.id === selector);
  const prefixMatch = companies.find((c) => c.issuePrefix.toUpperCase() === selector.toUpperCase());
  return !(idMatch && prefixMatch && idMatch.id !== prefixMatch.id);
}

Prevention

When it happens

Trigger: A selector like 'PAP' that is simultaneously a company's issue prefix and another company's id (e.g. an id that happens to read 'PAP'). Auto mode with by='auto' (default) and both lookups hit different rows.

Common situations: Short ids or prefixes that collide across the company set. Renamed prefixes that now match an existing id. Multi-tenant setups where prefixes and ids overlap by coincidence.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/aa0a95dae133e4e5. Report an issue: GitHub.