nikivdev/code · error · anyhow::Error

multiple matches for '{}': {}. Use owner/repo.

Error message

multiple matches for '{}': {}. Use owner/repo.

What it means

Raised when a bare repo `name` matches more than one locally cloned repo (src/deps.rs:1334), e.g. `foo` matching both `acme/foo` and `other/foo`. Because the tool cannot disambiguate, it bails and lists every matching `owner/repo` pair, asking the user to qualify the name.

Source

Thrown at src/deps.rs:1334

            });
        }
    }

    if matches.is_empty() {
        bail!(
            "repo '{}' not found under {}. Use owner/repo or run: f repos clone <url>",
            name,
            root.display()
        );
    }

    if matches.len() > 1 {
        let options = matches
            .iter()
            .map(|repo| format!("{}/{}", repo.owner, repo.repo))
            .collect::<Vec<_>>()
            .join(", ");
        bail!(
            "multiple matches for '{}': {}. Use owner/repo.",
            name,
            options
        );
    }

    Ok(matches.remove(0))
}

fn upsert_repo_manifest(path: &Path, root: &str, repo: &repos::RepoRef, url: &str) -> Result<()> {
    let mut doc = if path.exists() {
        let contents = std::fs::read_to_string(path)
            .with_context(|| format!("failed to read {}", path.display()))?;
        toml::from_str::<Value>(&contents).unwrap_or(Value::Table(Map::new()))
    } else {
        Value::Table(Map::new())
    };

View on GitHub (pinned to a747e741ae)

Solutions

  1. Retry with the qualified form shown in the message, e.g. `acme/foo`
  2. Remove or rename the duplicate clone you no longer need
  3. Keep a unique local directory name when cloning (clone into an owner-prefixed directory)
  4. Update any scripts/aliases to use owner/repo form

Example fix

// before
f deps open foo
error: multiple matches for 'foo': acme/foo, other/foo. Use owner/repo.
// after
f deps open acme/foo
Defensive patterns

Strategy: validation

Validate before calling

let short = name.rsplit('/').next().unwrap();
let count = std::fs::read_dir(repos_root)?.filter_map(|e| e.ok())
    .filter(|e| e.file_name().to_string_lossy() == short).count();
if count > 1 && !name.contains('/') {
    anyhow::bail!("'{name}' is ambiguous ({count} matches); use owner/repo");
}

Type guard

fn is_unambiguous(name: &str, repos_root: &Path) -> bool {
    if name.contains('/') { return true; }
    let short = name.rsplit('/').next().unwrap_or(name);
    std::fs::read_dir(repos_root).map(|rd| {
        rd.filter_map(|e| e.ok())
          .filter(|e| e.file_name().to_string_lossy() == short).count()
    }).map(|c| c <= 1).unwrap_or(true)
}

Try / catch

match resolve_repo(name) {
    Err(e) if e.to_string().contains("multiple matches for") => {
        // parse the listed options and pick/prompt
        let options = e.to_string().split(": ").nth(1).unwrap_or("");
        eprintln!("disambiguate: {options}");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling a repo-targeting command with an unqualified name while the repos root contains two or more repos whose leaf name equals `name`; also happens after forking/renaming clones under different owners.

Common situations: Forking a repo so both `origin` and your fork exist locally, two teams owning same-named repos, or bare-name shortcuts that used to be unique before a new clone was added.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/74d75a70ced2b268. Report an issue: GitHub.