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
- Upgrade turborepo to pick up path-handling fixes
- In downstream Rust code, construct these types only via try_from/new so invariants hold
- 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
- Construct AbsoluteSystemPathBuf only via new/try_from so the absolute-UTF-8 invariant holds
- Never assemble these path types by hand through From<String> on unvalidated input
- Contain turborepo-paths calls behind a process boundary in host applications
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
- clean should produce valid UTF-8: {err:?}
- clean should preserve utf8: {err:?}
- path cleaning should preserve UTF-8: {err:?}
- collapsed path should be absolute: {err:?}
- anchored system path is relative: {}
AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17).
Data as JSON: /api/errors/eec963370dc0a8dc.
Report an issue: GitHub.