{"record":{"id":"2c95455693b23ee3","repo":"jdx/mise","slug":"workspace-project-depends-on-unknown-project","errorCode":null,"errorMessage":"workspace project {:?} depends on unknown project {:?}","messagePattern":"workspace project (.+?) depends on unknown project (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/workspace.rs","lineNumber":1046,"sourceCode":"            if !visited.insert(dependency_id.clone()) {\n                continue;\n            }\n            self.collect_matching_dependencies(dependency_id, visited, matches, matching);\n            let dependency = self\n                .projects\n                .get(dependency_id)\n                .expect(\"workspace project graph is validated\");\n            if matches(dependency) {\n                matching.push(dependency);\n            }\n        }\n    }\n\n    fn validate(&self) -> Result<()> {\n        for project in self.projects() {\n            for dependency in &project.dependencies {\n                if self.get(dependency).is_none() {\n                    bail!(\n                        \"workspace project {:?} depends on unknown project {:?}\",\n                        project.id,\n                        dependency\n                    );\n                }\n            }\n        }\n        if let Some(path) = self.find_cycle() {\n            return Err(WorkspaceProjectCycleError { path }.into());\n        }\n        Ok(())\n    }\n\n    fn find_cycle(&self) -> Option<Vec<ProjectId>> {\n        let mut visited = BTreeSet::new();\n        let mut active = BTreeMap::new();\n        let mut path = Vec::new();\n        for id in self.projects.keys() {","sourceCodeStart":1028,"sourceCodeEnd":1064,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/task/workspace.rs#L1028-L1064","documentation":"After provider discovery and [monorepo.projects] overrides are applied, WorkspaceProjectGraph::validate() checks every dependency edge: each ID in a project's dependencies must itself be a project in the graph. A dangling edge — a project depending on an ID that was never discovered — aborts graph construction.","triggerScenarios":"An override like [monorepo.projects.\"cargo:app\".depends_add] naming an ID that does not exist (typo, missing provider prefix, or target not discovered); provider-inferred edges to excluded members (a Cargo workspace.dependencies path dependency whose crate is in exclude, a go.work module dependency on a module outside the use list); removing a project with remove = true while other projects still depend on it is handled by edge-stripping, but a depends entry pointing at an explicitly removed project re-added via depends_add is not.","commonSituations":"Writing depends_add = [\"shared\"] and forgetting the \"cargo:\" prefix; pointing at a crate that Cargo.toml excludes; a package.json/glob filter that drops a member other members import; edges surviving from a renamed project.","solutions":["Fix the dependency ID: include the provider prefix and exact local ID (\"cargo:shared\", \"go:example.com/shared\")","Make the target discoverable: list it in [workspace] members / go.work use / workspace globs, or remove it from exclude","Drop the edge with depends_remove instead of depending on a nonexistent project","If the project should exist outside providers, add it via an override with root = \"path\" so it enters the graph before validation"],"exampleFix":"# before (mise.toml)\n[monorepo.projects.\"cargo:app\"]\ndepends_add = [\"shared\"]\n\n# after\n[monorepo.projects.\"cargo:app\"]\ndepends_add = [\"cargo:shared\"]\n# and ensure Cargo.toml [workspace] members/exclude actually include crates/shared","handlingStrategy":"validation","validationCode":"// before calling discover_all_with_overrides\nfor (raw_id, cfg) in &overrides {\n    let id = raw_id.parse::<ProjectId>()?;\n    for dep in cfg.depends.as_ref().into_iter().flatten().chain(&cfg.depends_add) {\n        let dep = dep.parse::<ProjectId>()?;\n        if graph.get(&dep).is_none() && !overrides.contains_key(dep.as_str()) {\n            warn!(\"override for {id} references unknown dependency {dep}\");\n        }\n    }\n}","typeGuard":"fn override_edges_are_resolvable(graph: &WorkspaceProjectGraph, overrides: &BTreeMap<String, WorkspaceProjectOverride>) -> Vec<String> {\n    let mut problems = Vec::new();\n    for (raw, cfg) in overrides {\n        for dep in cfg.depends.as_ref().into_iter().flatten().chain(&cfg.depends_add) {\n            let ok = dep.parse::<ProjectId>().map(|d| graph.get(&d).is_some() || overrides.contains_key(d.as_str())).unwrap_or(false);\n            if !ok { problems.push(format!(\"{raw} -> {dep}\")); }\n        }\n    }\n    problems\n}","tryCatchPattern":"if let Err(err) = WorkspaceProjectGraph::discover_all_with_overrides(&providers, root, &overrides) {\n    if err.to_string().contains(\"depends on unknown project\") {\n        return user_facing_config_error(err, \"check [monorepo.projects] depends/depends_add entries\");\n    }\n    return Err(err);\n}","preventionTips":["Always write dependency IDs with the provider prefix (\"cargo:shared\"), copying the exact local ID from discovery","Keep depends_add targets within discovered members; excluded crates cannot be dependency targets","After renaming a project, sweep [monorepo.projects] for the old ID in depends/depends_add of other entries"],"tags":["rust","mise","workspace","config","dependency-graph"],"backgroundTag":"dangling-dependency-reference","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}