jdx/mise · error
unknown workspace project
Error message
unknown workspace project {id:?} What it means
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.
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`
Example fix
// before workspace.mark_changed(&["old-project"]); // after let ids: Vec<_> = changed.into_iter().filter(|id| workspace.projects.contains_key(id)).collect(); workspace.mark_changed(&ids);
Defensive patterns
Strategy: validation
Validate before calling
let known: Vec<_> = changed.iter().filter(|id| workspace.projects.contains_key(*id)).collect();
if known.len() != changed.len() {
eprintln!("dropping unknown project ids from changed set");
} Type guard
fn is_known_project<'a>(ws: &Workspace, id: &'a str) -> bool { ws.projects.contains_key(id) } Try / catch
match workspace.mark_changed(&changed) {
Ok(affected) => affected,
Err(e) if e.to_string().contains("unknown workspace project") => {
workspace = Workspace::reload()?; // refresh discovery, then retry
workspace.mark_changed(&changed)?
}
Err(e) => return Err(e),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- lockfile attributed to unknown workspace project
- workspace provider attributed lockfile to unknown project
- affected project exists in graph
- cannot encode # as JSON
- Cargo workspace member pattern
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/de61c4a29a041a92.
Report an issue: GitHub.
Appendix: source
Thrown at src/task/workspace.rs:819
pub(crate) fn affected_projects<'a>(
&self,
changed: impl IntoIterator<Item = &'a ProjectId>,
) -> Result<BTreeSet<ProjectId>> {
let mut dependents = BTreeMap::<ProjectId, BTreeSet<ProjectId>>::new();
for project in self.projects() {
for dependency in &project.dependencies {
dependents
.entry(dependency.clone())
.or_default()
.insert(project.id.clone());
}
}
let mut affected = BTreeSet::new();
let mut pending = Vec::new();
for id in changed {
if !self.projects.contains_key(id) {
bail!("unknown workspace project {id:?}");
}
if affected.insert(id.clone()) {
pending.push(id.clone());
}
}
while let Some(id) = pending.pop() {
for dependent in dependents.get(&id).into_iter().flatten() {
if affected.insert(dependent.clone()) {
pending.push(dependent.clone());
}
}
}
Ok(affected)
}
/// Maps changed paths to affected projects, including workspace-global inputs.
///View on GitHub (pinned to 533346cc37)