rust-lang/rust-analyzer · error

project root must point to a Cargo.toml, rust-project.json o

Error message

project root must point to a Cargo.toml, rust-project.json or <script>.rs file: {path}

What it means

ProjectManifest::from_manifest_file only accepts a Cargo.toml, a rust-project.json, or a `<script>.rs` file as the project root. Any other path (directory, missing manifest, wrong file) fails this bail. It tells you the path you handed to project discovery isn't a recognizable project manifest.

Source

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

}

impl ProjectManifest {
    pub fn from_manifest_file(path: AbsPathBuf) -> anyhow::Result<ProjectManifest> {
        let path = ManifestPath::try_from(path)
            .map_err(|path| format_err!("bad manifest path: {path}"))?;
        if path.file_name().unwrap_or_default() == "rust-project.json" {
            return Ok(ProjectManifest::ProjectJson(path));
        }
        if path.file_name().unwrap_or_default() == ".rust-project.json" {
            return Ok(ProjectManifest::ProjectJson(path));
        }
        if path.file_name().unwrap_or_default() == "Cargo.toml" {
            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>> {

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Point the path at the actual `Cargo.toml` of the crate/workspace.
  2. For non-Cargo setups, generate a `rust-project.json` and pass that file.
  3. For a single-file Rust script, pass the `<script>.rs` path.
  4. Verify the path exists and is a file, not a directory, before calling.

Example fix

// before
let manifest = ProjectManifest::from_manifest_file(&root_dir.join("project"))?;
// after
let manifest = ProjectManifest::from_manifest_file(&root_dir.join("project/Cargo.toml"))?;
Defensive patterns

Strategy: validation

Validate before calling

fn looks_like_manifest(p: &Path) -> bool {
    p.file_name().map_or(false, |f| f == "Cargo.toml" || f == "rust-project.json")
        || p.extension().map_or(false, |e| e == "rs")
}
// assert!(path.is_file() && looks_like_manifest(&path));

Try / catch

match ProjectManifest::from_manifest_file(&path) {
    Ok(m) => m,
    Err(e) if e.to_string().contains("project root must point") => {
        // walk up to find Cargo.toml or prompt user for the manifest path
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling `ProjectManifest::from_manifest_file(&path)` with: a directory path, a path whose file_name is not `Cargo.toml`, a file whose extension is not `rs` and is not rust-project.json, or a nonexistent file.

Common situations: Passing the workspace root directory instead of the manifest; pointing rust-analyzer at a non-Cargo (e.g. bazel) project without a rust-project.json; typo'd manifest path; running rust-analyzer outside a Rust project.

Related errors


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