{"record":{"id":"de61c4a29a041a92","repo":"jdx/mise","slug":"unknown-workspace-project-id","errorCode":null,"errorMessage":"unknown workspace project {id:?}","messagePattern":"unknown workspace project (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/workspace.rs","lineNumber":819,"sourceCode":"    pub fn affected_projects<'a>(\n        &self,\n        changed: impl IntoIterator<Item = &'a ProjectId>,\n    ) -> Result<BTreeSet<ProjectId>> {\n        let mut dependents = BTreeMap::<ProjectId, BTreeSet<ProjectId>>::new();\n        for project in self.projects() {\n            for dependency in &project.dependencies {\n                dependents\n                    .entry(dependency.clone())\n                    .or_default()\n                    .insert(project.id.clone());\n            }\n        }\n\n        let mut affected = BTreeSet::new();\n        let mut pending = Vec::new();\n        for id in changed {\n            if !self.projects.contains_key(id) {\n                bail!(\"unknown workspace project {id:?}\");\n            }\n            if affected.insert(id.clone()) {\n                pending.push(id.clone());\n            }\n        }\n\n        while let Some(id) = pending.pop() {\n            for dependent in dependents.get(&id).into_iter().flatten() {\n                if affected.insert(dependent.clone()) {\n                    pending.push(dependent.clone());\n                }\n            }\n        }\n        Ok(affected)\n    }\n\n    /// Maps changed paths to affected projects, including workspace-global inputs.\n    ///","sourceCodeStart":801,"sourceCodeEnd":837,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/task/workspace.rs#L801-L837","documentation":"WorkspaceProjectGraph::affected_projects() computes the set of changed projects plus every project that transitively depends on them. It fails fast when any passed ProjectId is not a key in the graph's project map. Project IDs are provider-namespaced strings (e.g. \"cargo:my-crate\", \"go:example.com/mod\") minted during discovery from Cargo.toml, go.work, package.json, or pyproject.toml, so any ID that was not discovered in the current run is rejected.","triggerScenarios":"Calling graph.affected_projects(ids) with an ID that is not in self.projects: an ID string that was cached or saved from an earlier run (crate/module renamed since), an ID whose project was removed via a [monorepo.projects.\"<id>\"] remove = true override, an ID from a provider that failed lenient discovery (provider_errors retained), or a hand-typed ID with a wrong provider prefix or local part.","commonSituations":"Stale cached project IDs after renaming a Cargo crate or Go module path; referencing a project that a colleague removed from workspace members; monorepo config drift between branches; a typo like \"cargo:my-crate \" (whitespace) or \"my-crate\" (missing provider prefix).","solutions":["Verify the ID exists first with graph.get(&id) and log/skip unknown IDs instead of failing","Re-derive the ID list from the current graph (graph.projects()) rather than reusing IDs captured in a previous session or cache","Check [monorepo.projects] overrides in mise.toml for a remove = true entry on that ID","Confirm the owning provider actually discovered the project (member listed in Cargo.toml [workspace].members / go.work use / pnpm workspace globs, not excluded)"],"exampleFix":"// before\nlet affected = graph.affected_projects([\"cargo:renamed_crate\".parse()?])?;\n\n// after\nlet id = \"cargo:renamed_crate\".parse()?;\nif graph.get(&id).is_none() {\n    warn!(\"project {id} no longer in workspace graph; skipping\");\n    return Ok(Default::default());\n}\nlet affected = graph.affected_projects([id])?;","handlingStrategy":"validation","validationCode":"let known: Vec<&ProjectId> = requested_ids.iter().filter(|id| graph.get(id).is_some()).collect();\nfor id in requested_ids.iter().filter(|id| graph.get(id).is_none()) {\n    warn!(\"skipping unknown workspace project {id}\");\n}\nlet affected = graph.affected_projects(known.iter().copied())?;","typeGuard":"fn is_known_project(graph: &WorkspaceProjectGraph, raw: &str) -> bool {\n    matches!(raw.parse::<ProjectId>(), Ok(id) if graph.get(&id).is_some())\n}","tryCatchPattern":"match graph.affected_projects(ids) {\n    Ok(set) => set,\n    Err(err) if err.to_string().contains(\"unknown workspace project\") => {\n        warn!(\"stale project reference: {err}\");\n        Default::default()\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Always source ProjectIds from graph.projects() of the same graph instance you query","Invalidate cached IDs whenever workspace manifests (Cargo.toml, go.work, package.json) change","Log graph.provider_discovery_error() before blaming ID inputs — a failed provider shrinks the graph"],"tags":["rust","mise","workspace","project-id","monorepo"],"backgroundTag":"unknown-project-reference","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}