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 instructions

View on GitHub (pinned to 47e5e902fa)

Solutions

  1. Grep all member Forc.toml files for '[project]' and make every name unique.
  2. Check the [workspace] members list does not include the same package twice (directly or via nested dirs).
  3. 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

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


AI-assisted analysis of FuelLabs/sway@47e5e902fa (2026-08-16). Data as JSON: /api/errors/f2494ce476580c21. Report an issue: GitHub.