FuelLabs/sway · error
graph contains more than one project node
Error message
graph contains more than one project node
What it means
find_proj_node found two or more graph nodes with the project's name that have no incoming edges. The 'single root' invariant is broken: there are multiple root nodes claiming the same package name, so compilation order and root selection are ambiguous.
Source
Thrown at forc-pkg/src/pkg.rs:896
/// yielding any nodes from the graph that might potentially be a project node.
fn potential_proj_nodes<'a>(g: &'a Graph, proj_name: &'a str) -> impl 'a + Iterator<Item = NodeIx> {
member_nodes(g).filter(move |&n| g[n].name == proj_name)
}
/// Given a graph, find the project node.
///
/// This should be the only node that satisfies the following conditions:
///
/// - The package name matches `proj_name`
/// - The node has no incoming edges, i.e. is not a dependency of another node.
fn find_proj_node(graph: &Graph, proj_name: &str) -> Result<NodeIx> {
let mut potentials = potential_proj_nodes(graph, proj_name);
let proj_node = potentials
.next()
.ok_or_else(|| anyhow!("graph contains no project node"))?;
match potentials.next() {
None => Ok(proj_node),
Some(_) => Err(anyhow!("graph contains more than one project node")),
}
}
/// Checks if the toolchain version is in compliance with minimum implied by `manifest`.
///
/// If the `manifest` is a ManifestFile::Workspace, check all members of the workspace for version
/// validation. Otherwise only the given package is checked.
fn validate_version(member_manifests: &MemberManifestFiles) -> Result<()> {
for member_pkg_manifest in member_manifests.values() {
validate_pkg_version(member_pkg_manifest)?;
}
Ok(())
}
/// Check minimum forc version given in the package manifest file
///
/// If required minimum forc version is higher than current forc version return an error with
/// upgrade instructionsView on GitHub (pinned to 47e5e902fa)
Solutions
- Grep all member Forc.toml files for '[project]' and make every name unique.
- Check the [workspace] members list does not include the same package twice (directly or via nested dirs).
- If a dependency legitimately shares the name, rename your project or the dependency reference, then regenerate Forc.lock.
Example fix
# before: two members with the same name # libs/a/Forc.toml -> name = "utils" # libs/b/Forc.toml -> name = "utils" # after # libs/b/Forc.toml -> name = "utils_v2" (and update dependents)
Defensive patterns
Strategy: validation
Validate before calling
let mut seen = std::collections::HashSet::new();
for m in member_manifests.values() {
if !seen.insert(m.project.name.clone()) {
return Err(format!("duplicate member name: {}", m.project.name));
}
} Type guard
fn member_names_are_unique(manifests: &forc_pkg::MemberManifestFiles) -> bool {
let mut seen = std::collections::HashSet::new();
manifests.values().all(|m| seen.insert(m.project.name.clone()))
} Prevention
- Name every workspace member uniquely from day one.
- When copying a member as a template, change [project] name before building.
When it happens
Trigger: Two workspace members with identical [project] name (both end up as roots); a path/registry dependency whose package name equals the project name and also sits at a root; duplicate member directories both listed in members.
Common situations: Copy-pasting a member folder for a new package and forgetting to change its name; renaming one member to another member's name; template reuse inside one workspace.
Related errors
- Cannot find manifest for {}
- graph contains no project node
- Cannot get parent dir of {:?}
- Couldn't find manifest file for {}
- failed to construct path for dependency {:?}: {}
AI-assisted analysis of FuelLabs/sway@47e5e902fa (2026-08-16).
Data as JSON: /api/errors/f2494ce476580c21.
Report an issue: GitHub.