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

  1. Validate that input is relative before constructing anchored paths (strip the repo root first)
  2. Use from_raw/checked constructors and reject absolute inputs early
  3. 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

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


AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17). Data as JSON: /api/errors/1997c61f16ccef2d. Report an issue: GitHub.