gleam-lang/gleam · error

Should not fail on two absolute paths

Error message

Should not fail on two absolute paths

What it means

`make_relative` panics when `pathdiff::diff_utf8_paths` returns None despite both paths being asserted absolute. pathdiff returns None when the two paths have different prefixes (notably on Windows: different drives like C:\ vs D:\), so the 'two absolute paths always relativizable' assumption is false.

Solutions

  1. Ensure source_path and target_path live on the same drive/volume (Windows).
  2. Handle the None case: fall back to the absolute target path instead of expecting.
  3. Check target_path.is_absolute() after diff and use the original absolute path when diffing fails.

Example fix

// before
pathdiff::diff_utf8_paths(target_path, source_path)
    .expect("Should not fail on two absolute paths")
// after
pathdiff::diff_utf8_paths(target_path, source_path)
    .unwrap_or_else(|| target_path.to_owned())
Defensive patterns

Strategy: try-catch

Validate before calling

let same_prefix = source_path.components().next() == target_path.components().next(); if target_path.is_absolute() && !same_prefix { /* expect failure on Windows */ }

Try / catch

// Rust panics cannot be caught except via catch_unwind
let rel = pathdiff::diff_utf8_paths(target, source).unwrap_or_else(|| target.to_owned());

Prevention

When it happens

Trigger: Calling `make_relative` (via `to_toml`) where source_path and target_path are absolute but on different Windows drives, or where one path's prefix differs (UNC vs drive letter), making diff_utf8_paths return None.

Common situations: Gleam projects on Windows with output/target on a different drive than the package source; publishing paths config (`gleam.toml` doc/output paths) spanning drives.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of gleam-lang/gleam@15b07c7830 (2026-09-14). Data as JSON: /api/errors/c3dcfa19232a1d7d. Report an issue: GitHub.

Appendix: source

Thrown at compiler-core/src/io.rs:58

/// Takes in a source path and a target path and determines a relative path
/// from source -> target.
/// If given a relative target path, no calculation occurs.
/// # Panics
/// The provided source path should be absolute, otherwise will panic.
pub fn make_relative(source_path: &Utf8Path, target_path: &Utf8Path) -> Utf8PathBuf {
    assert!(source_path.is_absolute());
    // Input target will always be canonicalised whereas source will not
    // This causes problems with diffing on windows since canonicalised paths have a special root
    // As such we are attempting to strip the target path
    // Based on https://github.com/rust-lang/rust/issues/42869#issuecomment-1712317081
    #[cfg(target_family = "windows")]
    let binding = target_path.to_string();
    #[cfg(target_family = "windows")]
    let target_path = Utf8Path::new(binding.trim_start_matches(r"\\?\"));

    match target_path.is_absolute() {
        true => pathdiff::diff_utf8_paths(target_path, source_path)
            .expect("Should not fail on two absolute paths"),

        false => target_path.into(),
    }
}

pub trait Reader: io::Read {
    /// A wrapper around `std::io::Read` that has Gleam's error handling.
    fn read_bytes(&mut self, buffer: &mut [u8]) -> Result<usize> {
        self.read(buffer).map_err(|error| self.convert_err(error))
    }

    fn convert_err<E: std::error::Error>(&self, error: E) -> Error;
}

pub trait Utf8Writer: std::fmt::Write {
    /// A wrapper around `fmt::Write` that has Gleam's error handling.
    fn str_write(&mut self, str: &str) -> Result<()> {
        self.write_str(str).map_err(|error| self.convert_err(error))

View on GitHub (pinned to 15b07c7830)