{"record":{"id":"cb2582745ad7c7d0","repo":"FuelLabs/sway","slug":"more-than-one-root-package-detected-in-graph","errorCode":null,"errorMessage":"more than one root package detected in graph","messagePattern":"more than one root package detected in graph","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"forc-pkg/src/pkg.rs","lineNumber":1297,"sourceCode":"    let proj_id = graph[proj_node].id();\n    manifest_map.insert(proj_id, proj_manifest.clone());\n\n    // Resolve all parents before their dependencies as we require the parent path to construct the\n    // dependency path. Skip the already added project node at the beginning of traversal.\n    let mut bfs = Bfs::new(graph, proj_node);\n    bfs.next(graph);\n    while let Some(dep_node) = bfs.next(graph) {\n        // Retrieve the parent node whose manifest is already stored.\n        let (parent_manifest, dep_name) = graph\n            .edges_directed(dep_node, Direction::Incoming)\n            .find_map(|edge| {\n                let parent_node = edge.source();\n                let dep_name = &edge.weight().name;\n                let parent = &graph[parent_node];\n                let parent_manifest = manifest_map.get(&parent.id())?;\n                Some((parent_manifest, dep_name))\n            })\n            .ok_or_else(|| anyhow!(\"more than one root package detected in graph\"))?;\n        let dep_path = dep_path(graph, parent_manifest, dep_node, manifests).map_err(|e| {\n            anyhow!(\n                \"failed to construct path for dependency {:?}: {}\",\n                dep_name,\n                e\n            )\n        })?;\n        let dep_manifest = PackageManifestFile::from_dir(&dep_path)?;\n        let dep = &graph[dep_node];\n        manifest_map.insert(dep.id(), dep_manifest);\n    }\n\n    Ok(manifest_map)\n}\n\n/// Given a `graph`, the node index of a path dependency within that `graph`, and the supposed\n/// `path_root` of the path dependency, ensure that the `path_root` is valid.\n///","sourceCodeStart":1279,"sourceCodeEnd":1315,"githubUrl":"https://github.com/FuelLabs/sway/blob/47e5e902faa42baf652dd6a0c88cd23390c1a614/forc-pkg/src/pkg.rs#L1279-L1315","documentation":"While BFS-walking the graph to build the manifest map, a visited dependency node had no incoming edge from an already-mapped parent. For the traversal to work, every non-root node must be reachable from the single project root - hitting this means the graph contains more than one root package's subtree (e.g. extra roots left by validation/pruning), making the parent ambiguous.","triggerScenarios":"The graph retains additional root nodes besides the project (multiple members acting as roots feeding shared dependencies); graph shaped unexpectedly after stale-lock validation or hand edits; duplicate package names causing nodes to be shared between roots.","commonSituations":"Workspace restructures with a stale Forc.lock; duplicated member names; partially pruned graphs after dependency removal.","solutions":["Regenerate Forc.lock (delete it or run 'forc update') and rebuild.","Verify member names are unique and each member is reachable only as declared.","If reproducible with a clean lock, capture the manifests and report upstream."],"exampleFix":"# before\nforc build   # more than one root package detected in graph\n\n# after\nrm Forc.lock && forc build","handlingStrategy":"try-catch","validationCode":"// sanity check before planning: exactly one root per member subgraph\nlet roots: Vec<_> = graph.externals(petgraph::Direction::Incoming).collect();\nif roots.len() > member_manifests.len() {\n    // extra roots present: regenerate Forc.lock before building\n}","typeGuard":null,"tryCatchPattern":"match build_plan_result {\n    Ok(plan) => { /* ... */ }\n    Err(e) if e.to_string().contains(\"more than one root package\") => {\n        let _ = std::fs::remove_file(&lock_path);\n        // rebuild once from manifests; escalate if it repeats\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Avoid duplicate member names, which let subtrees merge under multiple roots.","Regenerate the lock after removing members or dependencies so pruned nodes do not linger."],"tags":["graph","workspace","internal","lock-file"],"backgroundTag":null,"analyzedSha":"47e5e902faa42baf652dd6a0c88cd23390c1a614","analyzedAt":"2026-08-16T07:57:45.555Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}