{"record":{"id":"de61c4a29a041a92","repo":"jdx/mise","slug":"unknown-workspace-project-id","errorCode":null,"errorMessage":"unknown workspace project {id:?}","messagePattern":"unknown workspace project (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/workspace.rs","lineNumber":819,"sourceCode":"    pub(crate) 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/533346cc374382b41ec5ff70536252b2e96e725c/src/task/workspace.rs#L801-L837","documentation":"Methods that take a list of changed project IDs (e.g. to recompute affected projects/lockfiles) verify each ID exists in the workspace's project map. Passing an ID that was never discovered — or that no longer is — bails immediately rather than silently ignoring it.","triggerScenarios":"Calling the affected-projects/changed-IDs API with an ID string not produced by workspace discovery, or with a stale ID after config/providers changed and the project was removed.","commonSituations":"Caching project IDs across runs while the user deleted a project or edited workspace config; a typo in the ID; watch-mode events firing for a directory that is no longer a recognized project.","solutions":["Refresh the workspace (re-run discovery) so the project map is current before passing changed IDs","Validate each changed ID against the workspace's known project IDs and drop unknown ones","Fix the caller to only emit IDs obtained from `workspace.projects`"],"exampleFix":"// before\nworkspace.mark_changed(&[\"old-project\"]);\n// after\nlet ids: Vec<_> = changed.into_iter().filter(|id| workspace.projects.contains_key(id)).collect();\nworkspace.mark_changed(&ids);","handlingStrategy":"validation","validationCode":"let known: Vec<_> = changed.iter().filter(|id| workspace.projects.contains_key(*id)).collect();\nif known.len() != changed.len() {\n    eprintln!(\"dropping unknown project ids from changed set\");\n}","typeGuard":"fn is_known_project<'a>(ws: &Workspace, id: &'a str) -> bool { ws.projects.contains_key(id) }","tryCatchPattern":"match workspace.mark_changed(&changed) {\n    Ok(affected) => affected,\n    Err(e) if e.to_string().contains(\"unknown workspace project\") => {\n        workspace = Workspace::reload()?; // refresh discovery, then retry\n        workspace.mark_changed(&changed)?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always take changed IDs from the workspace's own project map","Reload/rediscover the workspace after config edits before using cached IDs","Treat project IDs as opaque handles, never hand-typed strings"],"tags":["workspace","not-found","project"],"backgroundTag":"entity-not-found","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}