{"record":{"id":"c3dcfa19232a1d7d","repo":"gleam-lang/gleam","slug":"should-not-fail-on-two-absolute-paths","errorCode":null,"errorMessage":"Should not fail on two absolute paths","messagePattern":"Should not fail on two absolute paths","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler-core/src/io.rs","lineNumber":58,"sourceCode":"/// Takes in a source path and a target path and determines a relative path\n/// from source -> target.\n/// If given a relative target path, no calculation occurs.\n/// # Panics\n/// The provided source path should be absolute, otherwise will panic.\npub fn make_relative(source_path: &Utf8Path, target_path: &Utf8Path) -> Utf8PathBuf {\n    assert!(source_path.is_absolute());\n    // Input target will always be canonicalised whereas source will not\n    // This causes problems with diffing on windows since canonicalised paths have a special root\n    // As such we are attempting to strip the target path\n    // Based on https://github.com/rust-lang/rust/issues/42869#issuecomment-1712317081\n    #[cfg(target_family = \"windows\")]\n    let binding = target_path.to_string();\n    #[cfg(target_family = \"windows\")]\n    let target_path = Utf8Path::new(binding.trim_start_matches(r\"\\\\?\\\"));\n\n    match target_path.is_absolute() {\n        true => pathdiff::diff_utf8_paths(target_path, source_path)\n            .expect(\"Should not fail on two absolute paths\"),\n\n        false => target_path.into(),\n    }\n}\n\npub trait Reader: io::Read {\n    /// A wrapper around `std::io::Read` that has Gleam's error handling.\n    fn read_bytes(&mut self, buffer: &mut [u8]) -> Result<usize> {\n        self.read(buffer).map_err(|e| self.convert_err(e))\n    }\n\n    fn convert_err<E: std::error::Error>(&self, error: E) -> Error;\n}\n\npub trait Utf8Writer: std::fmt::Write {\n    /// A wrapper around `fmt::Write` that has Gleam's error handling.\n    fn str_write(&mut self, str: &str) -> Result<()> {\n        self.write_str(str).map_err(|e| self.convert_err(e))","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/gleam-lang/gleam/blob/7e623aa83da3776faee50ca4ab9a6c40124acd95/compiler-core/src/io.rs#L40-L76","documentation":"make_relative (compiler-core/src/io.rs:45) computes a path from target back to source using pathdiff::diff_utf8_paths and expects Some whenever the target is absolute (the source is asserted absolute on the line above). pathdiff returns None when the two absolute paths have incompatible prefixes — on Windows that means different drive letters or a UNC vs local-drive mismatch (the `\\\\?\\` strip only removes the verbatim prefix, not drive differences). So this panic is a Windows-specific path-prefix mismatch, not general corruption.","triggerScenarios":"Windows builds where the canonicalised target path and the absolute source path live on different drives — e.g. project on C: but a package/build path resolved to D:, or a source path expressed as a UNC share (\\\\server\\share) while the target canonicalised to a mapped drive. `pathdiff::diff_utf8_paths(d:\\x, c:\\y)` → None → panic.","commonSituations":"Windows developers with GLEAM caches/build dirs redirected to another drive; projects on network shares or subst-mapped drives; CI on windows-lu runners where workspace and toolchain/virtual-store paths straddle drives.","solutions":["Keep every component of the project (sources, build dir, caches, git dependencies) on the same Windows drive — check where your HOME/HOMEDRIVE points if caches live there.","Replace UNC/mapped-drive paths with a local drive path (copy the project off the network share) before building.","If you call make_relative as a library, pre-check `pathdiff::diff_utf8_paths(target, source).is_none()` and fall back to using the absolute target path.","Report upstream: make_relative could gracefully fall back to the absolute target when prefixes differ instead of expect()."],"exampleFix":"// before: panics when prefixes differ (C: vs D:, UNC vs drive)\nlet rel = gleam_core::io::make_relative(&source, &target);\n\n// after: guard the incompatible-prefix case\nlet rel = match pathdiff::diff_utf8_paths(&target, &source) {\n    Some(diff) => diff,\n    None => target.to_path_buf(), // cross-drive: keep absolute\n};","handlingStrategy":"validation","validationCode":"// On Windows, verify the two paths share a prefix before diffing\nuse camino::Utf8Path;\n\nfn same_prefix(a: &Utf8Path, b: &Utf8Path) -> bool {\n    match (a.components().next(), b.components().next()) {\n        (Some(x), Some(y)) => x == y, // Prefix (drive/UNC) equality\n        _ => false,\n    }\n}\n\n// only call make_relative when compatible; otherwise use the absolute path\nlet rel = if same_prefix(source, target) {\n    make_relative(source, target)\n} else {\n    target.to_path_buf()\n};","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep project, build directory, and caches on one drive letter on Windows.","Avoid building from UNC network paths (\\\\server\\share) or subst-mapped drives.","Library callers: pre-check `pathdiff::diff_utf8_paths(target, source).is_some()` and fall back to the absolute target."],"tags":["panic","windows","path","pathdiff","cross-drive","unc","relative-path"],"backgroundTag":"path-prefix-mismatch","analyzedSha":"7e623aa83da3776faee50ca4ab9a6c40124acd95","analyzedAt":"2026-08-17T00:07:02.091Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}