vercel/turborepo · critical

joined path is absolute and valid utf8: {err:?}

Error message

joined path is absolute and valid utf8: {err:?}

What it means

AbsoluteSystemPathBuf::join_unix_path joins a relative Unix path onto an absolute system path, cleans the result, and converts it back into an AbsoluteSystemPathBuf. The try_into can only fail if the joined path were not absolute or not valid UTF-8 — both impossible given the input types — so this panic is an invariant guard inside turborepo-paths.

Source

Thrown at crates/turborepo-paths/src/absolute_system_path.rs:270

        debug_assert!(
            !segments
                .iter()
                .any(|segment| segment.contains(std::path::MAIN_SEPARATOR))
        );
        AbsoluteSystemPathBuf(clean_utf8_path(
            self.0.join(segments.join(std::path::MAIN_SEPARATOR_STR)),
        ))
    }

    pub fn as_str(&self) -> &str {
        self.0.as_str()
    }

    pub fn join_unix_path(&self, unix_path: impl AsRef<RelativeUnixPath>) -> AbsoluteSystemPathBuf {
        let tail = unix_path.as_ref().to_system_path_buf();
        AbsoluteSystemPathBuf(match self.0.join(tail).as_std_path().clean().try_into() {
            Ok(path) => path,
            Err(err) => panic!("joined path is absolute and valid utf8: {err:?}"),
        })
    }

    /// Joins a normalized relative Unix path without resolving `.` or `..`.
    /// Use `join_unix_path` unless the caller knows the path is normalized.
    pub fn join_unix_path_unchecked(
        &self,
        unix_path: impl AsRef<RelativeUnixPath>,
    ) -> AbsoluteSystemPathBuf {
        #[cfg(unix)]
        {
            AbsoluteSystemPathBuf(self.0.join(unix_path.as_ref().as_str()))
        }

        #[cfg(windows)]
        {
            let tail = unix_path.as_ref().to_system_path_buf();
            AbsoluteSystemPathBuf(self.0.join(tail))

View on GitHub (pinned to f9245100cf)

Solutions

  1. Upgrade turborepo to pick up path-handling fixes
  2. In downstream Rust code, construct these types only via try_from/new so invariants hold
  3. If reproducible, file an issue with the exact paths involved
Defensive patterns

Strategy: validation

Validate before calling

// Only join genuine relative Unix paths onto absolute bases
let tail = RelativeUnixPathBuf::new("pkg/lib")
    .map_err(|e| format!("tail must be relative: {e:?}"))?;
// base came from a checked constructor, so the join invariant holds
let joined = base.join_unix_path(tail);

Try / catch

let joined = std::panic::catch_unwind(|| base.join_unix_path(tail))
    .unwrap_or_else(|_| fallback_to_std_join(&base, &tail));

Prevention

When it happens

Trigger: Only via a bug or by bypassing the type's checked constructors (e.g., fabricating an AbsoluteSystemPathBuf from a relative string elsewhere in code) and then calling join_unix_path on it.

Common situations: Not reachable through turbo's public CLI; would appear as a crash in turbo's path handling after an internal regression.

Related errors


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