vercel/turborepo · critical
anchored system path is relative: {}
Error message
anchored system path is relative: {} What it means
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.
Source
Thrown at crates/turborepo-paths/src/anchored_system_path.rs:109
self.0.components()
}
pub fn as_path(&self) -> &Path {
self.0.as_std_path()
}
pub fn to_unix(&self) -> RelativeUnixPathBuf {
#[cfg(unix)]
let buf = RelativeUnixPathBuf::new(self.0.as_str());
#[cfg(not(unix))]
let buf = {
use crate::IntoUnix;
let unix_buf = self.0.into_unix();
RelativeUnixPathBuf::new(unix_buf)
};
buf.unwrap_or_else(|_| panic!("anchored system path is relative: {}", self.0.as_str()))
}
pub fn join_component(&self, segment: &str) -> AnchoredSystemPathBuf {
debug_assert!(!segment.contains(std::path::MAIN_SEPARATOR));
AnchoredSystemPathBuf(self.0.join(segment))
}
pub fn join_components(&self, segments: &[&str]) -> AnchoredSystemPathBuf {
debug_assert!(
!segments
.iter()
.any(|segment| segment.contains(std::path::MAIN_SEPARATOR))
);
let joined = self.0.join(segments.join(std::path::MAIN_SEPARATOR_STR));
AnchoredSystemPathBuf(clean_utf8_path(joined))
}
pub fn clean(&self) -> AnchoredSystemPathBuf {View on GitHub (pinned to f9245100cf)
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
Example fix
// before
let anchored = AnchoredSystemPathBuf::from_raw(user_input)?; // may be "C:\repo\pkg"
let unix = anchored.to_unix(); // panics on Windows
// after
let anchored = AnchoredSystemPathBuf::from_raw(user_input)?;
assert!(!anchored.as_str().contains(':'), "expected relative path");
let unix = anchored.to_unix(); Defensive patterns
Strategy: validation
Validate before calling
// On Windows, reject absolute/prefixed input before anchoring
let candidate = user_input.trim();
if candidate.contains(':') || candidate.starts_with('/') || candidate.starts_with('\\') {
return Err(format!("expected workspace-relative path, got {candidate}"));
}
let anchored = AnchoredSystemPathBuf::from_raw(candidate)?;
let unix = anchored.to_unix(); Try / catch
let unix = std::panic::catch_unwind(|| anchored.to_unix())
.map_err(|_| format!("anchored path is not relative: {}", anchored.as_str()))?; Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- joined path is absolute and valid utf8: {err:?}
- collapsed path should be absolute: {err:?}
- clean should produce valid UTF-8: {err:?}
- clean should preserve utf8: {err:?}
- path cleaning should preserve UTF-8: {err:?}
AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17).
Data as JSON: /api/errors/1997c61f16ccef2d.
Report an issue: GitHub.