rust-lang/rust-analyzer · error

more than one project

Error message

more than one project

What it means

discover_single requires that discovery find exactly one project. After popping one candidate, any remaining candidates cause this bail, because the API is meant for an unambiguous single-project root (e.g. a rust-project.json describing one workspace).

Source

Thrown at crates/project-model/src/lib.rs:126

            return Ok(ProjectManifest::CargoToml(path));
        }
        if path.extension().unwrap_or_default() == "rs" {
            return Ok(ProjectManifest::CargoScript(path));
        }
        bail!(
            "project root must point to a Cargo.toml, rust-project.json or <script>.rs file: {path}"
        );
    }

    pub fn discover_single(path: &AbsPath) -> anyhow::Result<ProjectManifest> {
        let mut candidates = ProjectManifest::discover(path)?;
        let res = match candidates.pop() {
            None => bail!("no projects"),
            Some(it) => it,
        };

        if !candidates.is_empty() {
            bail!("more than one project");
        }
        Ok(res)
    }

    pub fn discover(path: &AbsPath) -> io::Result<Vec<ProjectManifest>> {
        if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
            return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
        }
        if let Some(project_json) = find_in_parent_dirs(path, ".rust-project.json") {
            return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
        }
        return find_cargo_toml(path)
            .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());

        fn find_cargo_toml(path: &AbsPath) -> io::Result<Vec<ManifestPath>> {
            match find_in_parent_dirs(path, "Cargo.toml") {
                Some(it) => Ok(vec![it]),
                None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)),

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Delete or move the extra manifest (e.g. remove a stale rust-project.json next to Cargo.toml).
  2. Point discover_single at the specific crate's Cargo.toml directory.
  3. Switch to ProjectManifest::discover() and pick the manifest you want programmatically.

Example fix

// before
let manifest = ProjectManifest::discover_single(&monorepo_root)?; // ambiguous
// after
let manifest = ProjectManifest::discover_single(&monorepo_root.join("crates/mylib"))?;
Defensive patterns

Strategy: validation

Validate before calling

// count candidates before requesting a single project
let candidates = ProjectManifest::discover(&path)?;
if candidates.len() != 1 {
    // pick explicitly: e.g. prefer rust-project.json, or error out with choices
}

Try / catch

match ProjectManifest::discover_single(&path) {
    Ok(m) => m,
    Err(e) if e.to_string() == "more than one project" => {
        let mut c = ProjectManifest::discover(&path)?;
        c.retain(|m| matches!(m, ProjectManifest::CargoToml(_)));
        c.pop().ok_or_else(|| anyhow!("no usable manifest"))?
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling `ProjectManifest::discover_single(&abs_path)` where discovery finds multiple manifests — e.g. a directory containing both a rust-project.json and Cargo.toml files, or several candidate manifests in parent/nested dirs.

Common situations: Pointing discover_single at a workspace root that also has a rust-project.json and Cargo.toml; monorepo with multiple crate manifests where the caller expected one; leftover rust-project.json from a prior non-Cargo setup.

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/8ccabfa65a620542. Report an issue: GitHub.