jdx/mise · error
workspace path {path:?} escapes the workspace root
Error message
workspace path {path:?} escapes the workspace root What it means
While normalizing a workspace-relative path, normalize_workspace_path() rejects any '..' that pops past the path start: the path escapes the workspace root. This guards affected_projects_for_paths()/map_paths_to_projects() against out-of-tree paths like "../other-repo/file" that could otherwise never match a project root.
Source
Thrown at src/task/workspace.rs:1232
if normalized.as_os_str().is_empty() {
normalized.push(".");
}
Ok(normalized)
}
fn normalize_workspace_path(path: &Path) -> Result<PathBuf> {
if path.is_absolute() {
bail!("workspace path {path:?} is absolute; paths must be workspace-relative");
}
let mut normalized = PathBuf::new();
for component in path.components() {
match component {
Component::CurDir => {}
Component::Normal(component) => normalized.push(component),
Component::ParentDir => {
if !normalized.pop() {
bail!("workspace path {path:?} escapes the workspace root");
}
}
Component::RootDir | Component::Prefix(_) => {
bail!("workspace path {path:?} is absolute; paths must be workspace-relative");
}
}
}
if normalized.as_os_str().is_empty() {
normalized.push(".");
}
Ok(normalized)
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Strip the workspace-root prefix from absolute paths instead of building '../' relative paths
- Run git with -C <workspace-root> --relative --name-only so diffs are root-relative
- Filter out paths that strip_prefix(root) cannot resolve — they are outside the workspace
Example fix
# before mise$ git diff --name-only # emits ../../crates/app/src/lib.rs # after git -C /repo diff --relative --name-only # emits crates/app/src/lib.rs
Defensive patterns
Strategy: validation
Validate before calling
fn within_root(root: &Path, rel: &Path) -> bool {
let mut depth = 0;
rel.components().all(|c| match c {
std::path::Component::Normal(_) => { depth += 1; true }
std::path::Component::ParentDir => { let ok = depth > 0; depth = depth.saturating_sub(1); ok }
_ => true,
}) && root.join(rel).starts_with(root)
}
let safe: Vec<_> = paths.into_iter().filter(|p| within_root(root, p)).collect(); Type guard
fn path_does_not_escape(root: &Path, p: &Path) -> bool {
!p.is_absolute() && root.join(p).canonicalize().map(|c| c.starts_with(root)).unwrap_or(true)
} Prevention
- Never hand-build '../' paths for change lists; derive them via strip_prefix
- Generate diffs from the workspace root so output is already root-relative
- Treat out-of-root change events as unrelated to workspace projects
When it happens
Trigger: Passing changed-path lists containing '../' entries: output of git diff run in a subdirectory without --relative, file-watcher events outside the root, or manually constructed diffs combining directory changes with '..'.
Common situations: Running git diff inside a project subdirectory (paths come out as ../../style); watching a parent directory that contains the workspace; joining relative paths incorrectly before calling the API.
Related errors
- workspace project {id:?} has root {root:?} that escapes the
- workspace project {id:?} has absolute root {root:?}; roots m
- workspace path {path:?} is absolute; paths must be workspace
- Cargo workspace member pattern {member:?} must be relative
- mise upgrade --monorepo is not implemented yet
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/1f9a74dd950e76a1.
Report an issue: GitHub.