{"record":{"id":"d5a7f90a633f1797","repo":"jdx/mise","slug":"workspace-path-path-is-absolute-paths-must-be","errorCode":null,"errorMessage":"workspace path {path:?} is absolute; paths must be workspace-relative","messagePattern":"workspace path (.+?) is absolute; paths must be workspace-relative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/workspace.rs","lineNumber":1222,"sourceCode":"                    );\n                }\n            }\n            Component::RootDir | Component::Prefix(_) => {\n                bail!(\n                    \"workspace project {id:?} has absolute root {root:?}; roots must be workspace-relative\"\n                );\n            }\n        }\n    }\n    if normalized.as_os_str().is_empty() {\n        normalized.push(\".\");\n    }\n    Ok(normalized)\n}\n\nfn normalize_workspace_path(path: &Path) -> Result<PathBuf> {\n    if path.is_absolute() {\n        bail!(\"workspace path {path:?} is absolute; paths must be workspace-relative\");\n    }\n\n    let mut normalized = PathBuf::new();\n    for component in path.components() {\n        match component {\n            Component::CurDir => {}\n            Component::Normal(component) => normalized.push(component),\n            Component::ParentDir => {\n                if !normalized.pop() {\n                    bail!(\"workspace path {path:?} escapes the workspace root\");\n                }\n            }\n            Component::RootDir | Component::Prefix(_) => {\n                bail!(\"workspace path {path:?} is absolute; paths must be workspace-relative\");\n            }\n        }\n    }\n    if normalized.as_os_str().is_empty() {","sourceCodeStart":1204,"sourceCodeEnd":1240,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/task/workspace.rs#L1204-L1240","documentation":"normalize_workspace_path() validates every path handed to map_paths_to_projects() / affected_projects_for_paths(): paths must be workspace-relative, and an absolute path is rejected up front. Internally mise always strips the workspace-root prefix before calling these APIs, so a developer calling the graph API directly with filesystem paths (e.g. from a file watcher) hits this check.","triggerScenarios":"Calling graph.map_paths_to_projects([\"/home/me/repo/src/main.rs\"]) or affected_projects_for_paths(root, [absolute_path], ...) with un-stripped absolute paths; feeding paths from notify/inotify watchers, git diff --name-only run from another cwd, or canonicalized paths.","commonSituations":"Integrating file-watch/build tooling that natively produces absolute paths; assuming the API takes workspace-rooted absolute paths; inconsistent cwd between path production and graph use.","solutions":["Strip the workspace root first: path.strip_prefix(workspace_root) before passing","Emit relative paths at the source (e.g. git -C root diff --relative --name-only)","Filter or skip paths outside the workspace root — they cannot map to projects anyway"],"exampleFix":"// before\nlet map = graph.map_paths_to_projects([\"/repo/crates/app/src/lib.rs\"])?;\n\n// after\nlet rel = Path::new(\"/repo/crates/app/src/lib.rs\").strip_prefix(\"/repo\")?;\nlet map = graph.map_paths_to_projects([rel])?;","handlingStrategy":"validation","validationCode":"let rel_paths: Vec<PathBuf> = paths.iter()\n    .filter_map(|p| p.strip_prefix(workspace_root).ok().map(Path::to_path_buf))\n    .collect();\nlet map = graph.map_paths_to_projects(rel_paths)?;","typeGuard":"fn all_paths_workspace_relative<'a>(root: &Path, paths: impl IntoIterator<Item = &'a Path>) -> bool {\n    paths.into_iter().all(|p| !p.is_absolute())\n}","tryCatchPattern":"match graph.affected_projects_for_paths(root, paths, global_inputs) {\n    Ok(set) => set,\n    Err(err) if err.to_string().contains(\"is absolute\") => {\n        let rel: Vec<_> = paths.into_iter().filter_map(|p| p.strip_prefix(root).ok()).collect();\n        graph.affected_projects_for_paths(root, rel, global_inputs)?\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Convert watcher/diff output with strip_prefix(workspace_root) before calling graph APIs","Run git as git -C <workspace-root> diff --relative --name-only","Drop paths outside the workspace root — they can never map to a project"],"tags":["rust","mise","workspace","path-validation","file-watcher"],"backgroundTag":"absolute-path-rejected","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}