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
- Fix permissions/ownership on the path reported (`sudo chown -R $USER:$USER ~/.cargo` or equivalent).
- Remove or recreate a corrupted/broken-symlink cache directory so Cargo can rebuild it.
- 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
- Maintain consistent ownership of `~/.cargo` (avoid sudo writes).
- Periodically clean corrupted cache dirs so Cargo can rebuild them.
- Monitor disk health; treat recurring non-NotFound read_dir errors as fs warnings.
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
- unable to read .cargo-ok file at
- cache expected 4 bytes for index schema version
- failed to open
- maximum limit reached when reading
- path at ` ` was not valid utf-8
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 whenView on GitHub (pinned to eb98b54bc9)