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

  1. Canonicalize both inputs to the same form before calling: turn the relative path into an absolute one (join with cwd) or make `to` relative.
  2. If `to` is absolute, no fix is needed — the function already returns it unchanged.
  3. 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

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


AI-assisted analysis of sxyazi/yazi@5f901b886b (2026-09-02). Data as JSON: /api/errors/dff52a1bcf224813. Report an issue: GitHub.