sxyazi/yazi · error
Paths must be both absolute or both relative: {from:?} and {
Error message
Paths must be both absolute or both relative: {from:?} and {to:?} What it means
`path_relative_to_impl` computes a relative path from `from` to `to`, but only when both share the same absoluteness. If `from` is relative and `to` is absolute (the asymmetric case), the operation is ill-defined and the code bails with this message (an absolute `to` is returned directly instead). It protects callers from meaningless cross-base relativization.
Source
Thrown at yazi-fs/src/path/relative.rs:21
use anyhow::{Result, bail};
use yazi_shared::path::{PathBufDyn, PathCow, PathDyn, PathLike};
pub fn path_relative_to<'a, 'b, P, Q>(from: P, to: Q) -> Result<PathCow<'b>>
where
P: Into<PathCow<'a>>,
Q: Into<PathCow<'b>>,
{
path_relative_to_impl(from.into(), to.into())
}
fn path_relative_to_impl<'a>(from: PathCow<'_>, to: PathCow<'a>) -> Result<PathCow<'a>> {
use yazi_shared::path::Component::*;
if from.is_absolute() != to.is_absolute() {
return if to.is_absolute() {
Ok(to)
} else {
bail!("Paths must be both absolute or both relative: {from:?} and {to:?}");
};
}
if from == to {
return Ok(PathDyn::with_str(from.kind(), ".").into());
}
let (mut f_it, mut t_it) = (from.components(), to.components());
let (f_head, t_head) = loop {
match (f_it.next(), t_it.next()) {
(Some(RootDir), Some(RootDir)) => {}
(Some(Prefix(a)), Some(Prefix(b))) if path_prefix_eq(a, b) => {}
(Some(Prefix(_) | RootDir), _) | (_, Some(Prefix(_) | RootDir)) => {
return Ok(to);
}
(None, None) => break (None, None),
(a, b) if a != b => break (a, b),
_ => (),View on GitHub (pinned to 5f901b886b)
Solutions
- Canonicalize both inputs to the same form before calling: turn the relative path into an absolute one (join with cwd) or make `to` relative.
- If `to` is absolute, no fix is needed — the function already returns it unchanged.
- Check `Path::is_absolute()` on both arguments and branch in caller code.
Example fix
// before
let rel = path_relative_to("/base", "child/file").unwrap();
// after
let to_abs = std::env::current_dir().unwrap().join("child/file");
let rel = path_relative_to("/base", to_abs).unwrap(); Defensive patterns
Strategy: validation
Validate before calling
if from.is_absolute() != to.is_absolute() && !to.is_absolute() {
panic!("both paths must be absolute or both relative");
} Type guard
fn can_relativize(from: &Path, to: &Path) -> bool {
from.is_absolute() == to.is_absolute() || to.is_absolute()
} Try / catch
let rel = match path_relative_to(from, to) {
Ok(p) => p,
Err(e) if e.to_string().starts_with("Paths must be both absolute") => {
to.to_path_buf() // absolute target returned as-is
}
Err(e) => return Err(e),
}; Prevention
- Canonicalize all paths to absolute form (join with cwd) before relativizing.
- Assert argument absoluteness in tests around path computation helpers.
- Never mix config-supplied absolute URLs with cwd-derived relative paths without normalizing.
When it happens
Trigger: Calling `path_relative_to(from, to)` where `from.is_absolute() != to.is_absolute()` and `to` is relative — e.g. `path_relative_to("/base/dir", "file.txt")`.
Common situations: Mixing a cwd-derived relative path with a config-supplied absolute URL (or vice versa); passing user input paths that were never canonicalized; plugin/Lua code passing an absolute target into an API expecting relative inputs.
Related errors
- Invalid path
- invalid trash item path
- invalid trash info path
- URL path kind does not match Auth kind
- Failed to join new name with CWD
AI-assisted analysis of sxyazi/yazi@5f901b886b (2026-09-02).
Data as JSON: /api/errors/dff52a1bcf224813.
Report an issue: GitHub.