rust-lang/cargo · error · anyhow::Error

failed to read path

Error message

failed to read path `{path:?}`

What it means

`read_dir_with_filter` lists a directory, returning the names of entries passing a caller-supplied filter. It calls `path.read_dir()`; if the result is `Err`, it inspects the error kind: `NotFound` is tolerated and returns an empty `Vec` (the directory simply is not there), but any other I/O error (permissions, broken symlink, I/O failure) is wrapped with `.context(format!("failed to read path `{path:?}`"))` and propagated. This distinguishes 'absent cache dir' (normal) from 'unreadable cache dir' (real failure).

Solutions

  1. Fix permissions/ownership on the path reported (`sudo chown -R $USER:$USER ~/.cargo` or equivalent).
  2. Remove or recreate a corrupted/broken-symlink cache directory so Cargo can rebuild it.
  3. If the underlying disk/filesystem is failing, address the hardware/fs error, then re-run.

Example fix

# before — unreadable cache dir
$ ls ~/.cargo/registry/cache
ls: cannot open: Permission denied
$ cargo cache  # -> failed to read path

# after
$ sudo chown -R $USER:$USER ~/.cargo
$ cargo cache
Defensive patterns

Strategy: try-catch

Validate before calling

use std::path::Path;
fn readable_dir(p: &Path) -> bool {
    match std::fs::metadata(p) {
        Ok(m) => m.is_dir(),
        Err(_) => false,
    }
}
// before cache operations, confirm the dir exists and is readable

Try / catch

match path.read_dir() {
    Ok(entries) => { /* proceed */ }
    Err(e) if e.kind() == std::io::ErrorKind::NotFound => { /* treat as empty */ }
    Err(e) => {
        // permissions/symlink/io failure — surface to user for remediation
        return Err(anyhow::Error::new(e).context(format!("failed to read path `{path:?}`")));
    }
}

Prevention

When it happens

Trigger: Global cache tracking enumerating a cargo cache directory (`read_dir`) where the call fails with an error kind other than `NotFound` — e.g. `PermissionDenied`, a broken symlink, or a low-level I/O error. The non-NotFound arm fires.

Common situations: Corrupted cache directories, permission/ownership issues on `~/.cargo` subdirs (e.g. after running with sudo), dangling symlinks left by interrupted downloads, or disk/filesystem errors during `cargo cache`/cache cleanup operations.

Related errors


AI-assisted analysis of rust-lang/cargo@eb98b54bc9 (2026-08-11). Data as JSON: /api/errors/dd14c5d606f98645. Report an issue: GitHub.

Appendix: source

Thrown at src/workspace/global_cache_tracker.rs:681

    fn list_dir_names(path: &Path) -> CargoResult<Vec<String>> {
        Self::read_dir_with_filter(path, &|entry| {
            entry.file_type().map_or(false, |ty| ty.is_dir())
        })
    }

    /// Returns a list of names in a directory, filtered by the given callback.
    fn read_dir_with_filter(
        path: &Path,
        filter: &dyn Fn(&std::fs::DirEntry) -> bool,
    ) -> CargoResult<Vec<String>> {
        let entries = match path.read_dir() {
            Ok(e) => e,
            Err(e) => {
                if e.kind() == std::io::ErrorKind::NotFound {
                    return Ok(Vec::new());
                } else {
                    return Err(
                        anyhow::Error::new(e).context(format!("failed to read path `{path:?}`"))
                    );
                }
            }
        };
        let names = entries
            .filter_map(|entry| entry.ok())
            .filter(|entry| filter(entry))
            .filter_map(|entry| entry.file_name().into_string().ok())
            .collect();
        Ok(names)
    }

    /// Synchronizes the database to match the files on disk.
    ///
    /// This performs the following cleanups:
    ///
    /// 1. Remove entries from the database that are missing on disk.
    /// 2. Adds missing entries to the database that are on disk (such as when

View on GitHub (pinned to eb98b54bc9)