coreybutler/nvm-windows · error
failed to read source directory: %w
Error message
failed to read source directory: %w
What it means
First step of copyDir(): os.ReadDir(old) lists the source directory. This error means the source could not be listed — it does not exist, it is not a directory (a file occupies the name), or the user lacks list permission. No destination writes have happened when this fires.
Source
Thrown at src/utility/rename.go:90
// Copy file permissions
info, err := srcFile.Stat()
if err != nil {
return fmt.Errorf("failed to get source file info: %w", err)
}
err = os.Chmod(new, info.Mode())
if err != nil {
return fmt.Errorf("failed to set permissions on destination file: %w", err)
}
return nil
}
// copyDir recursively copies a directory from old path to new path.
func copyDir(old, new string) error {
entries, err := os.ReadDir(old)
if err != nil {
return fmt.Errorf("failed to read source directory: %w", err)
}
// Ensure destination directory exists
err = os.MkdirAll(new, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create destination directory: %w", err)
}
for _, entry := range entries {
srcPath := filepath.Join(old, entry.Name())
destPath := filepath.Join(new, entry.Name())
if entry.IsDir() {
err = copyDir(srcPath, destPath)
if err != nil {
return fmt.Errorf("failed to copy subdirectory: %w", err)
}
} else {View on GitHub (pinned to 5b18223ca1)
Solutions
- Verify the source directory still exists immediately before the operation (dir <path>).
- Ensure only one nvm command runs at a time to avoid races deleting the tree.
- Run elevated if the wrapped error is 'Access is denied'.
- Fix the NVM_HOME setting if it points at a stale/inconsistent layout.
Defensive patterns
Strategy: validation
Validate before calling
func isReadableDir(p string) bool {
info, err := os.Stat(p)
return err == nil && info.IsDir()
}
if !isReadableDir(src) { return fmt.Errorf("source %s is not a readable directory", src) } Type guard
func isReadableDir(p string) bool {
info, err := os.Stat(p)
return err == nil && info.IsDir()
} Try / catch
if err := copyTree(old, new); err != nil && strings.Contains(err.Error(), "read source directory") {
if os.IsNotExist(errors.Unwrap(err)) { /* source deleted mid-run: restart operation */ }
} Prevention
- Validate the directory exists and is a directory before recursive copies.
- Serialize nvm commands to prevent concurrent tree deletion.
- Fix NVM_HOME when the version tree layout looks inconsistent.
When it happens
Trigger: copyDir called with a path that was deleted between the parent's ReadDir and the recursive call, a path that names a regular file, or a directory with a denying ACL.
Common situations: Concurrent nvm operations pruning version directories; source directory moved/renamed manually mid-upgrade; NVM_HOME misconfigured to a path where a file shadows a directory name.
Related errors
- failed to copy directory: %w
- failed to get source file info: %w
- failed to copy subdirectory: %w
- failed to get source file info: %v
- failed to copy file: %w
AI-assisted analysis of coreybutler/nvm-windows@5b18223ca1 (2026-08-15).
Data as JSON: /api/errors/5974da79572046c6.
Report an issue: GitHub.