rust-lang/rust-analyzer · error

We explicitly do not provide canonicalization API, as that i

Error message

We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430

What it means

The `paths` crate intentionally does not expose a canonicalization API. `AbsPath::canonicalize` is declared with the never type `!` and unconditionally panics, steering developers away from canonicalization, which the maintainers consider almost always the wrong solution (see issue #14430, e.g. because it resolves symlinks and changes identity of paths in ways that break caching and correctness assumptions).

Source

Thrown at crates/paths/src/lib.rs:251

    ///
    /// # Example
    /// ```ignore
    /// # use paths::AbsPathBuf;
    /// let abs_path_buf = AbsPathBuf::assert("/a/../../b/.//c//".into());
    /// let normalized = abs_path_buf.normalize();
    /// assert_eq!(normalized, AbsPathBuf::assert("/b/c".into()));
    /// ```
    pub fn normalize(&self) -> AbsPathBuf {
        AbsPathBuf(normalize_path(&self.0))
    }

    /// Equivalent of [`Utf8Path::to_path_buf`] for `AbsPath`.
    pub fn to_path_buf(&self) -> AbsPathBuf {
        AbsPathBuf::try_from(self.0.to_path_buf()).unwrap()
    }

    pub fn canonicalize(&self) -> ! {
        panic!(
            "We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430"
        )
    }

    /// Equivalent of [`Utf8Path::strip_prefix`] for `AbsPath`.
    ///
    /// Returns a relative path.
    pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> {
        self.0.strip_prefix(base).ok().map(RelPath::new_unchecked)
    }
    pub fn starts_with(&self, base: &AbsPath) -> bool {
        self.0.starts_with(&base.0)
    }
    pub fn ends_with(&self, suffix: &RelPath) -> bool {
        self.0.ends_with(&suffix.0)
    }

    pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> {

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Do not canonicalize; use `AbsPath::normalize`-style logical normalization or `Path::absolutize`-like logic if you only need `..` removal
  2. Use `std::path::Path::canonicalize` on the underlying path directly if you truly need symlink resolution, then convert back via `AbsPathBuf::try_from`
  3. Reconsider the design: rust-analyzer deliberately avoids canonicalization because it breaks path identity expectations; model your problem without it

Example fix

// before
let canon = abs_path.canonicalize();
// after
let canon: std::io::Result<AbsPathBuf> =
    std::fs::canonicalize(abs_path.as_ref()).and_then(|p| Ok(AbsPathBuf::try_from(p)?));
Defensive patterns

Strategy: validation

Validate before calling

fn safe_canonicalize(p: &AbsPath) -> std::io::Result<AbsPathBuf> {
    std::fs::canonicalize(p.as_ref()).map(|q| AbsPathBuf::try_from(q).unwrap())
}

Type guard

// AbsPath exposes no canonicalize; guard by feature-checking in your own code
fn supports_canonicalize() -> bool { false }

Prevention

When it happens

Trigger: Calling `AbsPath::canonicalize()` (or `AbsPathBuf::canonicalize()`) anywhere in code that depends on the `paths` crate. The method always panics; there is no input that makes it succeed.

Common situations: Migrating code from `std::path::Path::canonicalize` or `camino` usage to rust-analyzer's `AbsPath` API; trying to normalize `..` components or resolve symlinks before passing paths to the analyzer.

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/2652b78ddc76b546. Report an issue: GitHub.