{"record":{"id":"1997c61f16ccef2d","repo":"vercel/turborepo","slug":"anchored-system-path-is-relative","errorCode":null,"errorMessage":"anchored system path is relative: {}","messagePattern":"anchored system path is relative: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/turborepo-paths/src/anchored_system_path.rs","lineNumber":109,"sourceCode":"        self.0.components()\n    }\n\n    pub fn as_path(&self) -> &Path {\n        self.0.as_std_path()\n    }\n\n    pub fn to_unix(&self) -> RelativeUnixPathBuf {\n        #[cfg(unix)]\n        let buf = RelativeUnixPathBuf::new(self.0.as_str());\n\n        #[cfg(not(unix))]\n        let buf = {\n            use crate::IntoUnix;\n            let unix_buf = self.0.into_unix();\n            RelativeUnixPathBuf::new(unix_buf)\n        };\n\n        buf.unwrap_or_else(|_| panic!(\"anchored system path is relative: {}\", self.0.as_str()))\n    }\n\n    pub fn join_component(&self, segment: &str) -> AnchoredSystemPathBuf {\n        debug_assert!(!segment.contains(std::path::MAIN_SEPARATOR));\n        AnchoredSystemPathBuf(self.0.join(segment))\n    }\n\n    pub fn join_components(&self, segments: &[&str]) -> AnchoredSystemPathBuf {\n        debug_assert!(\n            !segments\n                .iter()\n                .any(|segment| segment.contains(std::path::MAIN_SEPARATOR))\n        );\n        let joined = self.0.join(segments.join(std::path::MAIN_SEPARATOR_STR));\n        AnchoredSystemPathBuf(clean_utf8_path(joined))\n    }\n\n    pub fn clean(&self) -> AnchoredSystemPathBuf {","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/vercel/turborepo/blob/f9245100cf0d31d96628804ead485f6bf226e55a/crates/turborepo-paths/src/anchored_system_path.rs#L91-L127","documentation":"AnchoredSystemPathBuf represents a workspace-relative path. to_unix() converts it to a RelativeUnixPathBuf; on Unix this cannot fail, but on Windows the conversion goes through IntoUnix, which rejects absolute or drive-prefixed strings. The panic \"anchored system path is relative\" fires when an AnchoredSystemPathBuf was constructed from an absolute path, breaking the type's core invariant.","triggerScenarios":"On Windows, building an AnchoredSystemPathBuf from an absolute or drive-prefixed string (e.g. \"C:\\repo\\pkg\") by bypassing checked construction, then calling to_unix() on it.","commonSituations":"User-supplied absolute paths parsed as if they were repo-relative; cross-platform code paths that only test the Unix branch where the bug is silent.","solutions":["Validate that input is relative before constructing anchored paths (strip the repo root first)","Use from_raw/checked constructors and reject absolute inputs early","Upgrade turborepo for hardened path construction"],"exampleFix":"// before\nlet anchored = AnchoredSystemPathBuf::from_raw(user_input)?; // may be \"C:\\repo\\pkg\"\nlet unix = anchored.to_unix(); // panics on Windows\n// after\nlet anchored = AnchoredSystemPathBuf::from_raw(user_input)?;\nassert!(!anchored.as_str().contains(':'), \"expected relative path\");\nlet unix = anchored.to_unix();","handlingStrategy":"validation","validationCode":"// On Windows, reject absolute/prefixed input before anchoring\nlet candidate = user_input.trim();\nif candidate.contains(':') || candidate.starts_with('/') || candidate.starts_with('\\\\') {\n    return Err(format!(\"expected workspace-relative path, got {candidate}\"));\n}\nlet anchored = AnchoredSystemPathBuf::from_raw(candidate)?;\nlet unix = anchored.to_unix();","typeGuard":null,"tryCatchPattern":"let unix = std::panic::catch_unwind(|| anchored.to_unix())\n    .map_err(|_| format!(\"anchored path is not relative: {}\", anchored.as_str()))?;","preventionTips":["Always strip the workspace root (pathdiff/relation_to_path) before constructing anchored paths","Test Windows code paths in CI — Unix builds will not catch absolute-input bugs"],"tags":["rust","panic","path","windows","invariant"],"backgroundTag":"path-invariant-panic","analyzedSha":"f9245100cf0d31d96628804ead485f6bf226e55a","analyzedAt":"2026-08-17T10:46:15.696Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}