pnpm/pnpm · error · std::io::Error
CAFS shard path {} exists but does not resolve to a director
Error message
CAFS shard path {} exists but does not resolve to a directory What it means
When initializing the content-addressable store (CAFS), pacquet ensures each shard directory exists. If a shard path exists but Path::is_dir() - which follows symlinks - is false (a regular file, a non-directory symlink, or a broken symlink occupies it), initialization fails fast with AlreadyExists and the shard path. This upfront rejection prevents a much less actionable raw `open` error later during per-file CAFS writes.
Source
Thrown at pnpm/crates/store-dir/src/store_dir.rs:245
let shard_dir = files.join(format!("{shard:02x}"));
if let Err(error) = std::fs::create_dir(&shard_dir) {
if error.kind() != std::io::ErrorKind::AlreadyExists {
return Err(error);
}
// `AlreadyExists` is benign only when the existing
// entry resolves to a directory — a parallel pnpm
// or pacquet process racing the same layout is
// fine, and a symlink pointing at a real directory
// is too (ops folks occasionally spread a store
// across disks that way). `Path::is_dir` follows
// symlinks, which is the desired semantics here. A
// regular file, a non-dir symlink, or a broken
// symlink would make `mark_shard_ensured` a lie and
// punt the failure to a much less actionable
// `open` error inside the per-file CAFS write.
// Reject upfront.
if !shard_dir.is_dir() {
return Err(std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
format!(
"CAFS shard path {} exists but does not resolve to a directory",
shard_dir.display(),
),
));
}
}
self.mark_shard_ensured(shard);
}
Ok(())
}
}
#[cfg(test)]
mod tests;
View on GitHub (pinned to 6261b7f388)
Solutions
- Inspect the exact path printed in the message (ls -l) to see what occupies it
- If it is a stray file or broken symlink, delete it and rerun the install so the shard directory is created
- If the symlink was intentional, fix it to point at an existing directory on a mounted disk
- As a last resort, move the whole store aside and let pnpm recreate it (costs re-fetching)
Example fix
# before ls -l ~/.local/share/pnpm/store/v10/files/3f # -rw-r--r-- 1 me me 4096 ... 3f (a file where a dir is needed) # after rm ~/.local/share/pnpm/store/v10/files/3f pnpm install
Defensive patterns
Strategy: validation
Validate before calling
fn store_shards_ensure_ok(root: &Path) -> Result<(), String> {
for shard in 0u8..=255 {
let p = root.join(format!("files/{:02x}", shard));
match std::fs::symlink_metadata(&p) {
Ok(m) if m.is_dir() || m.file_type().is_symlink() && p.is_dir() => {}
Ok(_) => return Err(format!("shard path occupied by non-directory: {}", p.display())),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
Err(e) => return Err(e.to_string()),
}
}
Ok(())
} Type guard
fn is_shard_not_a_directory(err: &std::io::Error) -> bool {
err.kind() == std::io::ErrorKind::AlreadyExists
&& err.to_string().contains("does not resolve to a directory")
} Try / catch
if let Err(e) = store_dir.ensure() {
if is_shard_not_a_directory(&e) {
// path is in the message: inspect, remove the stray file/broken link, retry once
} else { return Err(e); }
} Prevention
- Pre-flight shard layout validation when automating store setup
- If the store is symlinked across disks, mount-check the target before installs
- Never let other tools write regular files into the store's files/ tree
When it happens
Trigger: Store initialization where a path like <store>/files/<shard> exists as a plain file or as a symlink that does not resolve to a directory: manual tampering, an interrupted store migration, or a symlinked store whose target disk was unmounted or moved.
Common situations: Store spread across disks via symlinks and the target volume removed; partial restore/copy of a store directory tree; another tool writing files into the store path; network filesystem with a dead link.
Related errors
- GITHUB_ACTIONS_WORKFLOW_OUTSIDE_ROOT
- PACK_APP_OUTPUT_FILE_NOT_REGULAR
- PROJECT_INACCESSIBLE
- Not implemented
- temporary directory must not be a symbolic link
AI-assisted analysis of pnpm/pnpm@6261b7f388 (2026-08-17).
Data as JSON: /api/errors/067fe88828474bfe.
Report an issue: GitHub.