coreybutler/nvm-windows · error

failed to copy subdirectory

Error message

failed to copy subdirectory: %w

What it means

Inside copyDir()'s entry loop, a nested directory is handled by a recursive copyDir(srcPath, destPath) call. This error means the recursion into a subdirectory failed; the wrapped chain (read source directory / create destination directory / copy file / ...) names the exact nested failure. Depth of nesting does not itself cause the error, but long resulting paths can.

Solutions

  1. Unwrap the error chain to the deepest cause and fix that specific entry (unlock file, free space, fix permissions).
  2. Stop node/npm processes and retry.
  3. Enable Windows long-path support for deep node_modules trees: set registry LongPathsEnabled=1.
  4. Run elevated and add an antivirus exclusion for NVM_HOME.
Defensive patterns

Strategy: retry

Validate before calling

// Pre-walk the tree to detect unreadable subdirectories before copying
filepath.WalkDir(old, func(p string, d fs.DirEntry, err error) error {
    if err != nil { return fmt.Errorf("pre-check failed at %s: %w", p, err) }
    if d.IsDir() { if _, e := os.ReadDir(p); e != nil { return e } }
    return nil
})

Try / catch

err := copyTree(old, new)
if err != nil && strings.Contains(err.Error(), "copy subdirectory") {
    killNodeProcesses()
    err = copyTree(old, new) // one retry after unlocking
}

Prevention

When it happens

Trigger: Recursive copy of a node version tree where some nested directory (e.g. node_modules\<pkg>\bin) cannot be read, its destination cannot be created, or any file inside it is locked/read-only.

Common situations: node_modules with deeply nested or locked packages; running node processes inside the tree; path length exceeding MAX_PATH (~260 chars) with long paths disabled; antivirus interference.

Related errors


AI-assisted analysis of coreybutler/nvm-windows@5b18223ca1 (2026-08-15). Data as JSON: /api/errors/b81a49cd6cab62c7. Report an issue: GitHub.

Appendix: source

Thrown at src/utility/rename.go:106

	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 {
			err = copyFile(srcPath, destPath)
			if err != nil {
				return fmt.Errorf("failed to copy file: %w", err)
			}
		}
	}

	return nil
}

View on GitHub (pinned to 5b18223ca1)