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
- Point the path at the actual `Cargo.toml` of the crate/workspace.
- For non-Cargo setups, generate a `rust-project.json` and pass that file.
- For a single-file Rust script, pass the `<script>.rs` path.
- 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
- Always pass a file path to the manifest, never a directory.
- Accept Cargo.toml / rust-project.json / script.rs only.
- Search parent directories for Cargo.toml before erroring.
- Validate path.is_file() before discovery.
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
- no projects
- more than one project
- Failed to parse `build.target` as an array of target
- No file available to rename
- Invalid name `{}`: cannot rename to a keyword
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/b79a1e9bf07619c6.
Report an issue: GitHub.