{"record":{"id":"067fe88828474bfe","repo":"pnpm/pnpm","slug":"cafs-shard-path-exists-but-does-not-resolve-to","errorCode":null,"errorMessage":"CAFS shard path {} exists but does not resolve to a directory","messagePattern":"CAFS shard path (.+?) exists but does not resolve to a directory","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"pnpm/crates/store-dir/src/store_dir.rs","lineNumber":245,"sourceCode":"            let shard_dir = files.join(format!(\"{shard:02x}\"));\n            if let Err(error) = std::fs::create_dir(&shard_dir) {\n                if error.kind() != std::io::ErrorKind::AlreadyExists {\n                    return Err(error);\n                }\n                // `AlreadyExists` is benign only when the existing\n                // entry resolves to a directory — a parallel pnpm\n                // or pacquet process racing the same layout is\n                // fine, and a symlink pointing at a real directory\n                // is too (ops folks occasionally spread a store\n                // across disks that way). `Path::is_dir` follows\n                // symlinks, which is the desired semantics here. A\n                // regular file, a non-dir symlink, or a broken\n                // symlink would make `mark_shard_ensured` a lie and\n                // punt the failure to a much less actionable\n                // `open` error inside the per-file CAFS write.\n                // Reject upfront.\n                if !shard_dir.is_dir() {\n                    return Err(std::io::Error::new(\n                        std::io::ErrorKind::AlreadyExists,\n                        format!(\n                            \"CAFS shard path {} exists but does not resolve to a directory\",\n                            shard_dir.display(),\n                        ),\n                    ));\n                }\n            }\n            self.mark_shard_ensured(shard);\n        }\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests;\n","sourceCodeStart":227,"sourceCodeEnd":262,"githubUrl":"https://github.com/pnpm/pnpm/blob/6261b7f388016d57ca6b90340342411cd1d0d00f/pnpm/crates/store-dir/src/store_dir.rs#L227-L262","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"# before\nls -l ~/.local/share/pnpm/store/v10/files/3f\n# -rw-r--r-- 1 me me 4096 ... 3f   (a file where a dir is needed)\n\n# after\nrm ~/.local/share/pnpm/store/v10/files/3f\npnpm install","handlingStrategy":"validation","validationCode":"fn store_shards_ensure_ok(root: &Path) -> Result<(), String> {\n    for shard in 0u8..=255 {\n        let p = root.join(format!(\"files/{:02x}\", shard));\n        match std::fs::symlink_metadata(&p) {\n            Ok(m) if m.is_dir() || m.file_type().is_symlink() && p.is_dir() => {}\n            Ok(_) => return Err(format!(\"shard path occupied by non-directory: {}\", p.display())),\n            Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}\n            Err(e) => return Err(e.to_string()),\n        }\n    }\n    Ok(())\n}","typeGuard":"fn is_shard_not_a_directory(err: &std::io::Error) -> bool {\n    err.kind() == std::io::ErrorKind::AlreadyExists\n        && err.to_string().contains(\"does not resolve to a directory\")\n}","tryCatchPattern":"if let Err(e) = store_dir.ensure() {\n    if is_shard_not_a_directory(&e) {\n        // path is in the message: inspect, remove the stray file/broken link, retry once\n    } else { return Err(e); }\n}","preventionTips":["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"],"tags":["filesystem","store","symlink","rust"],"backgroundTag":"not-a-directory","analyzedSha":"6261b7f388016d57ca6b90340342411cd1d0d00f","analyzedAt":"2026-08-17T18:30:54.750Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}